I am working with the Laravel 9 application. I have created a custom Exception. I want to report the General Exception to the sentry and this custom Exception to another vendor like Papertrail.
The Handler.php is not calling the reportable closure function when the application throws a custom exception i.e ServiceException.
By the way, the Laravel documentation for errors is also not understandable
Handler.php
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Inertia\Inertia;
use Throwable;
use Exception;
use App\Exceptions\ServiceException;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* #var array
*/
protected $dontReport = [
// ServiceException::class
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* #var array
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* #return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
// this block is calling for Exception but not for custom Exception like ServiceException.
if ($this->shouldReport($e) && app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
$this->reportable(function (ServiceException $e) {
// this block is not calling when I throw ServiceException.
echo "Send this ServiceException to papertrail app."; // this line is never called.
die;
});
}
public function render($request, Throwable $e)
{
$response = parent::render($request, $e);
if ($request->isJson()) {
//prevent error for local and staging.
if (! app()->environment(['local', 'staging']) && in_array($response->status(), [500, 503, 404, 403])) {
\Log::error('API Error Handler', [$response->getOriginalContent()]);
$message = trans('message.error_description_500');
if ($response->status() == 404) {
$message = trans('message.data_not_found');
} elseif ($response->status() == 403) {
$message = trans('message.you_are_not_allowed_perform');
} elseif ($response->status() == 503) {
$message = trans('message.error_description_503');
}
return response()->json([
'message' => $message,
], $response->status());
}
}
return $response;
}
}
ServiceException.php
<?php
namespace App\Exceptions;
use Exception;
class ServiceException extends Exception
{
/**
* Report the exception.
*
* #return void
*/
public function report()
{
//
}
/**
* Render the exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function render($request, $exception)
{
if (! $request->ajax()) {
// view('error_handler', compact('exception'));
}
return response()->json([
'code' => $exception->getCode(),
'status' => 'error',
'message' => $exception->getMessage(),
'data' => 'sample data',
]);
}
}
AnyController.php
public function anyFunction() {
// throw new Exception('Unhandled Exception.'); // It will call the $this->reportable(function (Throwable $e) { block.
// throw new ServiceException('Unhandled ServiceException.'); // It will call the $this->reportable(function (Throwable $e) { block.
try {
$this->service->aFunctionThatThrowException(); // this function will throw ServiceException.
} catch (Exception $e) {
Log::error('controller_fun_error', ['error' => $e->getMessage()]);
report($e);
return $this->failResponse();
}
}
AnyService.php
public function aFunctionThatThrowException() {
try {
throw new ServiceException('Throwing ServiceException...)');
} catch (ServiceException | Exception $e) {
Log::error('service_fun_error', ['error' => $e->getMessage()]);
throw $e;
}
}
I'm not intirely sure, but I don't think the error will be reported if you put a try catch around it yourself.
Create a new helper functions file eg: app/Helpers/Common.php and create a service provider HelperServiceProvider in app/Providers/HelperServiceProvider.php use the code below :
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
require_once __DIR__ . '/../Helpers/Common.php';
}
}
Now copy the below mentioned code to the app/Helpers/Common.php file :
if (! function_exists('throwResponse')) {
function throwResponse($message='Exceptions',$data=[],$statusCode=500){
if(request()->wantsJson()) {
if ((gettype($message) !== 'string') && ($message instanceof \Exception)) {
if($message->getMessage()){
$data = (!empty($message->getTrace())) ? $message->getTrace() : [];
$message = (!empty($message->getMessage())) ? $message->getMessage() : "Something went wrong";
$data = $data?:[$message];
$statusCode = 500;
}else{
throw new \Illuminate\Http\Exceptions\HttpResponseException($message->getResponse());
}
}
$errStatus = (in_array($statusCode,[200,201])) ? false : true;
$response = ['code'=>(int)$statusCode,
'error'=>$errStatus,
'message'=>$message];
if(!empty($data)){
$response['data'] = $data;
}
if($statusCode == 200 && $data == "empty"){
$response['data'] = [];
}
throw new \Illuminate\Http\Exceptions\HttpResponseException(response()->json($response,$statusCode));
} else{
if(is_object($message)){
throw $message;
}else{
return $message;
}
}
}
}
Now you can easily call the throwResponse function from anywhere in the project :
for normal response
throwResponse('Message for the response',['user_id'=>1],200);
{
"code":200,
"error":false,
"message":"Message for the response",
"data":{
"user_id":1
}
}
or
throwResponse('unauthorized',null,401);
{
"code":401,
"error":true,
"message":"unauthroized",
}
or for exceptions simply pass the exception $e
throwResponse($e);
{
"code":500,
"error":true,
"message":"exception message",
"data":{
exception data
}
}
The response will be
for example :
public function exampleFunction(Request $request){
try{
if(empty($request->userId)){
throwResponse('Validation error',null,401);
}
$userData = User::find($request->userId);
if(!empty($userData)){
throwResponse('User data',$userData,401);
}else{
throwResponse('Invalid user id',null,422);
}
}catch(\Exception $e){
throwResponse($e);
}
}
Change responses according to your need
Related
i am new to laravel
this is my code for controller
I am writing a code to save the data from the user in Mysql
And then redirect me to the list page with updated table and a flash message that the data has been updated successfully
i also tried using session->('key') but it didn't work
<?php
namespace App\Http\Controllers;
use App\restaurent_name;
use Illuminate\Http\Request;
class RestaController extends Controller
{
function Index()
{
return view('home');
}
function list()
{
$data = restaurent_name::all();
return view('list',["Data"=>$data]);
}
function add(Request $req)
{
//return $req->input();
$save = new restaurent_name;
$save->name=$req->input("name");
$save->address=$req->input("address");
$save->contact=$req->input("contact");
$save->save();
$save->session()->has('status');
//$save->session()->put('status', 'Task was successful!');
//$save->session()->flash('status','Restaurent added succesfully');
return redirect('/list');
}
}
the data gets saved properly into the database but the issue is that i am not able to receive message from flash session
This the error message i receive when i try to use flash session
`
Illuminate\Database\Eloquent\Model::throwBadMethodCallException C:\xampp\htdocs\Laravel
Projects\Restaurants\vendor\laravel\framework\src\Illuminate\Support\Traits\ForwardsCalls.php:50
$pattern = '~^Call to undefined method (?P<class>[^:]+)::(?P<method>[^\(]+)\(\)$~';
if (! preg_match($pattern, $e->getMessage(), $matches)) {
throw $e;
}
if ($matches['class'] != get_class($object) ||
$matches['method'] != $method) {
throw $e;
}
static::throwBadMethodCallException($method);
}
}
/**
* Throw a bad method call exception for the given method.
*
* #param string $method
* #return void
*
* #throws \BadMethodCallException
*/
protected static function throwBadMethodCallException($method)
{
throw new BadMethodCallException(sprintf(
'Call to undefined method %s::%s()', static::class, $method
));
}
}
`
You can do that with :
return redirect('/list')
->with('status', 'Restaurent added succesfully')
->with('status2', 'Task was successful!');
With Session Flash :
Session::flash('status', 'Restaurent added succesfully');
Session::flash('status2', 'Task was successful!');
return redirect('/list');
I can't find an explanation of why a return response() inside a catch is not stopping the execution, i guess im missing something.
In this example the request have an error so the try-catch on the service receive ValidationException. This goes to ValidationErrorResponder and here is where the execution should finish and return a json with the errors. But it continues and return the json error response through $this->updateUserResponder->respond()
I have a defined route which execute the __invoke() method on UpdateUserAction
class UpdateUserAction
{
protected $updateUserService;
protected $updateUserResponder;
public function __construct(UpdateUserService $updateUserService, UpdateUserResponder $updateUserResponder)
{
$this->updateUserService = $updateUserService;
$this->updateUserResponder = $updateUserResponder;
}
public function __invoke(Request $request, $userId)
{
$serviceData = [
'id' => $userId,
'commandPayload' => $request->only('name')
];
return $this->updateUserResponder->respond($this->updateUserService->execute($serviceData));
}
}
class UpdateUserService extends BaseService
{
public function execute(array $data = [])
{
try {
$this->bus->addHandler(UpdateUserCommand::class, UpdateUserHandler::class);
return $this->bus->dispatch(UpdateUserCommand::class, $data, [UpdateUserValidator::class]);
} catch (ValidationException $e) {
return $this->validationErrorResponder->respond($e);
}
}
}
class UpdateUserValidator implements Middleware
{
protected $rules = [
'id' => 'uuid',
'commandPayload.name' => 'max:256'
];
protected $messages = [];
public function execute($command, callable $next)
{
$validator = Validator::make((array) $command, $this->rules, $this->messages);
if ($validator->fails()) {
throw new ValidationException($validator);
}
return $next($command);
}
}
This shoudl return the final response wiht the errors in a JSON but
class ValidationErrorResponder
{
public function respond($validator)
{
$messages = $validator->getValidator()->getMessageBag()->messages();
return response()->json(['errors' => $messages], 422);
}
}
Maybe the error it's another and the catch does not working because only are catching ValidationException.
So try catching all exception to see what happens:
class UpdateUserService extends BaseService
{
public function execute(array $data = [])
{
try {
$this->bus->addHandler(UpdateUserCommand::class, UpdateUserHandler::class);
return $this->bus->dispatch(UpdateUserCommand::class, $data, [UpdateUserValidator::class]);
} catch (\Exception $e) {
return $this->validationErrorResponder->respond($e);
}
}
}
I have a class social which has:
protected $id;
public function __construct($request, $id)
{
Log::info('Processing...', ['request' => $request, 'id' => $id]);
try {
$client = new Client();
$url = sprintf($request);
$response = $client->get($url);
$json = json_decode((string) $response->getBody(), true);
return $json;
} catch (ClientException $exception) {
$responseBody = $exception->getResponse()->getBody(true);
Log::error($responseBody, ['entity_id' => $id]);
}
}
public function wikipedia($wikipedia_url, $id)
{
dd($json);
try {
$wikipedia_array = $json['parse']['text'];
$wikipedia_array = array_slice($wikipedia_array, 0, 9);
$wikipedia_array = implode($wikipedia_array, ',');
Log::info('Processed Wikipedia for', ['entity_id' => $id]);
return $wikipedia_array;
} catch (Exception $e) {
Log::error('Wikipedia:', ['message' => $e->getMessage(), 'entity_id' => $id]);
}
}
In another function I am calling a facade like this:
$id = $entity->id;
$wikipedia_id = $entity->wikipedia;
if (!empty($wikipedia_id)) {
$wikipedia_url = 'http://en.wikipedia.org/w/api.php?action=parse&prop=text§ion=0&disablelimitreport=1&format=json&page='.$wikipedia_id;
$wikipedia_html = Social::wikipedia($wikipedia_url, $id);
Log::info('Wikipedia ok for', ['entity_id' => $id]);
}
However I get this:
Type error: Too few arguments to function App\Helpers\Social::__construct(), 0 passed in /home/vagrant/liveandnow/app/Providers/SocialServiceProvider.php on line 35 and exactly 2 expected
Can anyone explain to me how to call a method, pass parameters to it but also pass them along to construct?
Here's my facade:
<?php
namespace App\Facade;
use Illuminate\Support\Facades\Facade;
class Social extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'social';
}
}
and service provider:
public function register()
{
$this->app->bind('social', function ($app) {
return new Social;
});
}
the error lies in the service provider.
You define a constructor with 2 parameters,
public function __construct($request, $id)
but in your service provider you call it like this:
public function register()
{
$this->app->bind('social', function ($app) {
return new Social;
});
}
You need to add both arguments when instantiating the Social class, for example like
return new Social("http://xyz.de", 1);
Hope this helps.
We have created an API that connects to the SOAP API system of the customer. We have a custom exception CustomerNotFound :
<?php
namespace App\Exceptions;
class CustomerNotFoundException extends \Exception
{
protected $code = 'customer_not_found';
protected $message = 'Customer not found!';
}
A Soap Class function for retrieving user information
public function getCustomerById($customerID)
{
$parameters = $this->appendToParameters([
'customerID' => $customerID])
->getParameters();
$result = $this->client->GetCustomerByID($parameters);
if ($result->SDKResult->ResultCode === 'Success') {
$data = $result->GetCustomerByIDResult;
return $data;
}
throw new CustomerNotFoundException();
}
And a controller that calls the soap class function:
public function getCustomerDetails(Request $request, $customerId)
{
try {
$customerDetails = $this->customerService->getCustomerById($customerId);
return $this->respond($customerDetails);
} catch (\SoapFault $e) {
$this->respondInternalError($e->getMessage());
} catch (\App\Exceptions\CustomerNotFoundException $c) {
$this->respondNotFound($c->getMessage());
}
}
I know the exception is working because when the soap function is also called on another controller, it works and then catches the exception properly when an invalid customer id is given. Also when I remove the try catch block and call the controller function with and invalid id, the api returns the CustomerNotFound exception. However, when it is inside a try catch block it just returns a STATUS 200 and an empty response (no jJSON, etc.). No errors are shown. Any ideas?
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Response;
use JWTAuth;
class ApiController extends Controller
{
protected $statusCode = 200;
public function isDevMode(){
return request()->get('__devmode__', null) == 'ph';
}
public function err($msg, $code = 'error'){
return $this->respond([
'data'=> null,
'status'=> [
'code'=> $code,
'msg'=> $msg,
'success'=> false
]
]);
}
public function ok($data =[],$msg='Success',$code='success'){
return $this->respond([
'data'=> $data,
'status'=> [
'code'=> $code,
'msg'=> $msg,
'success'=> true
]
]);
}
public function getStatusCode()
{
return $this->statusCode;
}
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
public function respondNotFound($message = 'Not Found.',$code='not_found')
{
return $this->setStatusCode(404)->respondWithError($message,$code);
}
public function respondInternalError($message = 'Internal Server Error.', $code='internal_server_error')
{
return $this->setStatusCode(500)->respondWithError($message,$code);
}
public function respond($data, $headers = ['Content-type'=> 'application/json; charset=utf-8'])
{
return Response::json($data, $this->getStatusCode(), $headers);
}
public function respondWithError($message, $code = 'error')
{
return $this->respond([
'error' => [
'code' => $code,
'message' => $message
// 'status_code' => $this->getStatusCode()
]
]);
}
public function getUserTokenDetails()
{
$token = JWTAuth::getToken()->get();
try {
if (! $user = JWTAuth::parseToken())
{
return response()->json(['user_not_found'], 404);
}
}
catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e)
{
$this->setStatusCode(401);
return $this->respondWithError('Token has expired.','token_expired');
}
catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e)
{
$this->setStatusCode(401);
return $this->respondWithError('Token is invalid','token_invalid');
}
catch (Tymon\JWTAuth\Exceptions\JWTException $e)
{
$this->setStatusCode(401);
return $this->respondWithError('Token not found.','token_not_found');
}
$payload = JWTAuth::parseToken()->getPayload();
$user = $payload['customClaims'];
return $user['CUST_ID'];
}
}
I have an application, which for each method it does a field validation before proceeding with code execution.
public function authenticateSeller(Request $request)
{
$fields = $request->all();
$rules = config("validation-rules.loads.access");
$validator = Validator::make($fields, $rules);
if($validator->fails()) {
throw new CustomFieldValidation($validator);
}
}
I needed to pass the object to the CustomFieldValidation class to pass to the Laravel Handler class to handle the errors and return in the JSON form. How to do this?
class CustomFieldValidation extends \Exception
{
public function __construct($validator,$message= NULL, $code = NULL, Exception $previous = NULL)
{
parent::__construct($message, $code, $previous);
}
}
I was hoping to manipulate the message handler's rendering method.
public function render($request, Exception $exception)
{
if($exception instanceof CustomFieldValidation) {
foreach($validator->errors()->all() as $message) {
$errors[] = [
'message' => $message,
];
}
return response()->json($errors, 400, ['Content-type' => 'application/json; charset=utf-8'], JSON_UNESCAPED_UNICODE);
}
return parent::render($request, $exception);
}
Can someone help me?
To answer your question: just add a $validator attribute to your exception class.
class CustomFieldValidation extends \Exception
{
public $validator;
public function __construct($validator,$message= NULL, $code = NULL, Exception $previous = NULL)
{
parent::__construct($message, $code, $previous);
$this->validator = $validator;
}
}
public function render($request, Exception $exception)
{
if($exception instanceof CustomFieldValidation) {
foreach($exception->validator->errors()->all() as $message) {
$errors[] = [
'message' => $message,
];
}
return response()->json($errors, 400, ['Content-type' => 'application/json; charset=utf-8'], JSON_UNESCAPED_UNICODE);
}
return parent::render($request, $exception);
}
#Samsquanch is right, the use case example you're showing is simple and it'd be better to just return JSON from the controller. However if you're throwing the exception from some other class, this would make sense.