I have a Laravel Job that is being dispatched. The job gets some records to process then calls an API with each. The API could occasionally could throw an error e.g. HTTP500 etc. This library (Goose) uses Guzzle.
I want to catch the error, flag the record to try again later and continue with the rest of the records in the loop.
My code is as follows but when an error occurs, the foreach loop ceases and the job does not continue.
My guess is that the Job Handler in Laravel is throwing a fatal error and the job is being halted at a higher level.
public function extractArticles() {
$articles = $this->fetchArticlesToExtract();
$goose = new GooseClient();
foreach($articles as $article) {
try {
//Get the Article Data - Errors can happen here
$articleData = $goose->extractContent($article->source_url);
//Do Some Processing
$article->save();
}
catch (\Goose\Exception $e) {
//Set a flag to come back later
//Try continue with next record
continue;
}
}
return;
}
How can I tell the Job to continue? There is only a small set of errors that can occur here and I have written the logic to 'try again', so I'm happy to not stop the job completely.
You have too many } and they are in the wrong places
public function extractArticles() {
$articles = $this->fetchArticlesToExtract();
$goose = new GooseClient();
foreach($articles as $article) {
try {
//Get the Article Data - Errors can happen here
$articleData = $goose->extractContent($article->source_url);
//Do Some Processing
$article->save();
}
//} removed
catch (\Goose\Exception $e) {
//Set a flag to come back later
//Try continue with next record
continue;
}
}
return;
}
Related
It's my first time to use DB::transaction() but how exactly does it work if a transaction fails or is successful? In the example below, do I have to manually assign a value to return true, or if it fails will the method either return false or totally exit the transaction (therefore skipping the rest of the code)? The docs aren't so helpful on this.
use Exception;
use DB;
try {
$success = DB::transaction(function() {
// Run some queries
});
print_r($success);
} catch(Exception $e) {
echo 'Uh oh.';
}
Solution
I wrote down this solution for others who might be wondering.
Since I was more concerned about returning a boolean value depending on the success of my query, with a few modifications it now returns true/false depending on its success:
use Exception;
use DB;
try {
$exception = DB::transaction(function() {
// Run queries here
});
return is_null($exception) ? true : $exception;
} catch(Exception $e) {
return false;
}
Take note that the variable $exception is never returned since if something goes wrong with your query, the catch is immediately triggered returning false. Thanks to #ilaijin for showing that an Exception object is thrown if something goes wrong.
By giving a look at function transaction it does its process inside a try/catch block
public function transaction(Closure $callback)
{
$this->beginTransaction();
// We'll simply execute the given callback within a try / catch block
// and if we catch any exception we can rollback the transaction
// so that none of the changes are persisted to the database.
try
{
$result = $callback($this);
$this->commit();
}
// If we catch an exception, we will roll back so nothing gets messed
// up in the database. Then we'll re-throw the exception so it can
// be handled how the developer sees fit for their applications.
catch (\Exception $e)
{
$this->rollBack();
throw $e;
}
So throws an Exception (after the rollback) if fails or returns $result, which is the result of your callback
There is a short version if you want to use the default transaction method that ships with Laravel without handling it manually.
$result = DB::transaction(function () {
// logic here
return $somethingYouWantToCheckLater;
});
You can also use the following
DB::rollback();
Couchbase keeps complaining that I don't have a connection to Couchbase:
2013-10-28T11:15:46.580320-07:00 hoot77 apache2[30455]: PHP Warning: There is no active connection to couchbase in /ebs1/www/src/core/components/In/Couchbase/Bucket.php on line 112
The following is the piece of code that is trying to run, its a simple set.
*/
public function set($key, $doc, $try = 0) {
// Make sure designDoc and dataView are set
if(empty($this->designDoc) && empty($this->dataView)) {
throw new In_Exception('Missing Design Doc and/or View name for Couchbase', 400);
}
try {
$results = $this->cb->set($key, $doc);
} catch(CouchbaseLibcouchbaseException $e) {
// Connection not active, try to rebuild connection and query again
// Log stats on exception
Statsd::increment("web.Couchbase.Exception.LibCouchbaseException.{$this->instanceKey}");
Logger::error("LibCouchbaseException on {$this->instanceKey}: {$e->getMessage()}");
// Try to reconnect and query up to twice
$try++;
if($try <= 2) {
$this->rebuildConnection();
return $this->set($key, $doc, $try);
} else {
// Fail if we've already tried twice
throw new In_Exception('Could not connect to Couchbase', 500);
}
} catch(CouchbaseException $e) {
// Catch general exception, try to determine cause
// Log stats on exception
Statsd::increment("web.Couchbase.Exception.CouchbaseException.{$this->instanceKey}");
Logger::error("CouchbaseException on {$this->instanceKey}: {$e->getMessage()}");
// Throw exception if design document or view not found
if($this->getExceptionCode($e->getMessage()) == 404) {
throw new In_Exception('Design Document, or View not found in Couchbase', 404);
} else {
throw new In_Exception('Error with Couchbase, try again.', 500);
}
}
// Success, return results
Statsd::increment("web.couchbase.{$this->instanceKey}.success");
return $results;
}
The line that its complaining about is:
$results = $this->cb->set($key, $doc);
You can see that my quick solution was to try to reconnect up to twice on failure, but that doesn't seem to be helping at all, the errors still persist in great numbers.
It's actually not catching the exceptions and reporting them consistently either, which is annoying, because PHP is throwing these as warnings, not exceptions.
Let me know if you have any suggestions as to how to solve this, it would be greatly appreciated. Thanks!
I can't get my head around WHEN to throw and catch exceptions for when I call functions from classes.
Please imagine that my QuizMaker class looks like this:
// Define exceptions for use in class
private class CreateQuizException extends Exception {}
private class InsertQuizException extends Exception {}
class QuizMaker()
{
// Define the items in my quiz object
$quiz_id = null;
$quiz_name = null;
// Function to create a quiz record in the database
function CreateQuizInDatabase()
{
try
{
$create_quiz = // Code to insert quiz
if (!$create_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting record");
}
else
{
// Return true, the quiz was created successfully
return true;
}
}
catch (CreateQuizException $create_quiz_exception)
{
// There was an error creating the quiz in the database
return false;
}
}
function InsertQuestions()
{
try
{
$insert_quiz = // Code to insert quiz
if (!$insert_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting quiz in to database");
}
else
{
// Success inserting quiz, return true
return true;
}
}
catch (InsertQuizException $insert_exception)
{
// Error inserting question
return false;
}
}
}
... and using this code, I use the class to create a new quiz in the database
class QuizMakerException extends Exception {}
try
{
// Create a blank new quiz maker object
$quiz_object = new QuizMaker();
// Set the quiz non-question variables
$quiz_object->quiz_name = $_POST['quiz_name'];
$quiz_object->quiz_intro = $_POST['quiz_intro'];
//... and so on ...
// Create the quiz record in the database if it is not already set
$quiz_object->CreateQuizRecord();
// Insert the quiz in to the database
$quiz_object->InsertQuestions();
}
catch (QuizMakerException $quiz_maker_error)
{
// I want to handle any errors from these functions here
}
For this piece of code, I want to call a QuizMakerException if any of the functions don't perform what I want them to (at the moment they return TRUE or FALSE).
What is the correct way to go about catching when any of the functions in this code does not perform what I want them to? At the moment they simply return TRUE or FALSE.
Do I really have to put lots of if/else statements between calling each function, I thought that was the whole point in exceptions, they simply halt the execution of further statements within the try/catch?
Do I throw a QuizMakerException from within the catch of my functions?
What is the right thing to do?
Help!
Well typically in the function which throws the exception, say your InsertQuestions method, you don't want to catch any exceptions, you want to throw them or let ones occurring to "bubble up". Then your "controller" code can make the determination of how to handle the exception.
If your goal here is to halt if CreateQuizRecord fails I would wrap CreateQuizRecord and InsertQuestions each in their own try block.
One advantage of exceptions is they can tell you more than a simple bool pass/fail. Either extending your base exception into things like "Invalid_Parameter" and testing for specific exceptions or -less ideally- inferring from properties of the exception. You can nest your catch blocks to handle exceptions individually.
Do I throw a QuizMakerException from within the catch of my functions?
Yes. Typically your code under // Code to insert quiz would itself return an exception. Say if the Model failed to insert it might be raising a database exception. In which case you can let that database exception bubble up, or do what you sort of doing now and catch it simply to in turn throw another exception (kinda dumbs down your exceptions in a way, doing that though).
Do I really have to put lots of if/else statements between calling each function, I thought that was the whole point in exceptions, they simply halt the execution of further statements within the try/catch?
I look at it like this, each call which throws an exception and is followed by a subsequent call which depends on this one not throwing any exceptions, should be wrapped in a try block. Assuming you want to handle it gracefully, if you simply want it to error out and halt just don't handle the exception. You'll get an error and stack trace. Sometimes that is desirable.
You might want to change the structure a bit. Your class QuizMaker can become:
<?php
// Define exceptions for use in class
public class CreateQuizException extends Exception {}
public class InsertQuizException extends Exception {}
class QuizMaker()
{
// Define the items in my quiz object
$quiz_id = null;
$quiz_name = null;
// Function to create a quiz record in the database
function CreateQuizInDatabase()
{
try
{
$create_quiz = // Code to insert quiz
if (!$create_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting record");
}
else
{
// Return true, the quiz was created successfully
return true;
}
}
catch (Exception $create_quiz_exception)
{
// There was an error creating the quiz in the database
throw new CreateQuizException(
"Failed inserting record " .
$create_quiz_exception->getMessage()
);
}
}
function InsertQuestions()
{
try
{
$insert_quiz = // Code to insert quiz
if (!$insert_quiz)
{
// There was an error creating record in the database
throw new InsertQuizException("Failed inserting quiz in to database");
}
else
{
// Success inserting quiz, return true
return true;
}
}
catch (Exception $insert_exception)
{
// Error inserting question
throw new InsertQuizException(
"Failed inserting quiz in to database " .
$create_quiz_exception->getMessage()
);
}
}
}
Effectively, if you cannot insert the record correctly, you throw an Insert exception. If anything goes wrong with that block of code, you catch it and throw again an Insert exception. Same goes with the Create function (or any other ones you might have).
In the main block of code you can have:
try
{
// Create a blank new quiz maker object
$quiz_object = new QuizMaker();
// Set the quiz non-question variables
$quiz_object->quiz_name = $_POST['quiz_name'];
$quiz_object->quiz_intro = $_POST['quiz_intro'];
//... and so on ...
// Create the quiz record in the database if it is not already set
$quiz_object->CreateQuizRecord();
// Insert the quiz in to the database
$quiz_object->InsertQuestions();
}
catch (InsertQuizException $insert_exception)
{
// Insert error
}
catch (CreateQuizException $create_quiz_exception)
{
// Create error
}
catch (Exception $quiz_maker_error)
{
// Any other error
}
If you don't want to have the multiple catch block there, just keep the catch(Exception) one and then check in it the type of each exception thrown to specify the actions taken from there.
HTH
The best thing to do is to not have to thhrow exceptions in the first place.
Exceptions are thrown when the program crashes and they are not made to handle wrong output.
If your function returns anything (even its the wrong thing) it doesn't need an exception.
To answer your question, if it's necessaryto use a lot of ifs/elses then you must use them.
Which is the right way to implement php exception (try{}catch(){}) in a foreach loop that looks like this:
foreach ($apis as $api)
{
$api = '_'.$api;
$searchResults[$api] = $this->$api($parameters);
}
I want to implement the php exceptions for if one of the $this->api(); returns an error message, than catch it and do a if inside the catch to display the right message for the error message returned.
Edit:
Also, when capturing the error and if the error message is 1 (for example) is it a good way to do:
$searchResults['api'] = $this->_api($parameters);
so it tries to do the function again and see if this time it brings valid data?
foreach ($apis as $api)
{
$api = '_'.$api;
try {
$searchResults[$api] = $this->$api($parameters);
}
catch(ParameterException $e) {
// parameterexception handling here
echo "A ParameterException was thrown";
}
catch(Exception $e) {
// All other exceptions
echo "Some other Exception was thrown";
}
}
You can differentiate between more Exception-Types as well.
Since the catch block will be executed only in case of an exception, it really makes no difference if you wrap the for-each loop inside the try block, or if you put the try-catch inside the loop's body.
You should take whatever adds more clarity. However, doing it inside the loop will enable you to handle more specific exceptions relevant to the loop's body if the need arises in the future.
Also, since exceptions are typed, you don't need to do an if, just put different catch clauses:
try {
:
} catch (FirstExceptionType $e) {
:
} catch (SecondExceptionType $e) {
:
}
The code you use will always fail, because you are trying to use a variable ($this->$api($params);) as a function. How ever, you could implement your try-catch as follows:
foreach ($apis as $api)
{
$api = '_'.$api;
try {
$searchResults[$api] = $this->$api($parameters);
}
catch(Exception $e) {
// handle exception
}
}
You can also handle multiple Exceptions of different types by adding another catch() with another Exception class inside it, like:
foreach ($apis as $api)
{
$api = '_'.$api;
try {
$searchResults[$api] = $this->$api($parameters);
}
catch(OtherException $e) {
// Handle it
}
catch(Exception $e) {
// Handle it
}
}
This question is in continuation from this as suggested by one of the user.
I am using the getIDs function as below to process the id's. CheckValid() will check if the id's is a valid one to be processed, if yes then it will go to the next one updateUsers(). Check valid just checks for a condition and if not it throws an exception. updateUsers() just updates a column if it passes checkValid().
Problem – If I get 4 id's as output from getIDs(), and with the execute(), it process 2 for example and if it fails for 2nd id, it doesn't continue for the rest 2 id's ..I want it to continue so I commented out the "throw $e in the catch block".
Function execute() {
for($i=0 ; $i<count($this->getIDs()); $i++) {
try {
$this->checkValid();
$this->updateUsers();
} catch(Exception $e) {
//throw $e;
}
have you try a simple continue in the catch block ? didn't test but maybe something like that:
Function execute() {
for($i=0 ; $i<count($this->getIDs()); $i++) {
try {
$this->checkValid();
$this->updateUsers();
} catch(Exception $e) {
//throw $e;
continue; // if not working try a continue 2;
}
}
}
It sounds like you're using exceptions as booleans, I'd suggest avoiding that, as it gets confusing quickly, unless you really need the contents of the exception. See if this makes any sense for your use case (I'll grant, it may not).
// returns true if valid, false otherwise
function checkValid(){
try {
// do your validation
return true;
} catch (Exception $e) {
// optional: save the exception in case we want to know about it
$this->last_error = $e;
return false;
}
}
function execute() {
for($i=0 ; $i<count($this->getIDs()); $i++) {
if($this->checkValid()){
$this->updateUsers();
}
// if you want to do something with an error, simply add an else clause
// and handle $this->last_error
}
}
Also, I obviously don't know your code or what exactly you're doing, but looping n times and calling checkValid() and updateUsers() without parameters seems like very poor practice. Much better to, for instance, loop over the list of IDs and check each ID and user in turn, something like this:
foreach($this->getIDs() as $id){
if($this->checkValid($id)){
$this->updateUser($id);
} else {
// an advantage of this is now we can know exactly which ID failed,
// because we have the $id variable
}
}