Undefined property: GuzzleHttp\Exception\ConnectException::$status - laravel

I keep getting this error when trying to get the status of the request.
This is my code
ExpenseRepository.php
<?php
namespace Expensetrim\Api\v1\Repositories;
use Auth;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Expensetrim\Models\Company;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ExpenseRepository
{
private $api_url;
const API_GET = 'GET';
const API_POST = 'POST';
const API_PUT = 'PUT';
function __construct()
{
$this->api_url = "http://xxx-xxx-xxx/api/v1/";
}
public function api_call($method, $uri, $request = NULL)
{
try {
$url=$this->api_url.$uri;
$client = new Client(['base_uri' => $this->api_url]);
$response = ($request) ? $client->request($method, $uri, $request) : $client->request($method, $uri);
}
catch (RequestException $e) {
return $e;
}
return $this->formatResponseBody($response);
}
public static function formatResponseBody($response)
{
$body = $response->getBody(true)->getContents();
return json_decode($body);
}
public function addExpenseType($data)
{
$uri = 'expense/types/add';
$response = $this->api_call(self::API_POST, $uri, ['form_params' => $data]);
return $response;
}
Also CompanyRepository.php
public function addExpenseType($company_id, $data)
{
$data['company_id'] = $company_id;
$expense = new ExpenseRepository;
$done = $expense->addExpenseType($data);
if($done->status == 'success') {
return true;
}
return true;
}
I need to check if the status is a success or not but keep getting this error: Undefined property: GuzzleHttp\Exception\ConnectException::$status.
Please what am i doing wrong?

There is an exception thrown at this line:
catch (RequestException $e) {
return $e;
}
and you are returning the exception. The return value of the method addExpenseType is actually an exception thrown by Guzzle.
throw the exception to see the error.
change your code to
catch (RequestException $e) {
throw $e;
}

Change your formatResponseBody function to add $response->getBody()->rewind();
public static function formatResponseBody($response)
{
$response->getBody()->rewind();
$body = $response->getBody(true)->getContents();
return json_decode($body);
}
In the old version of guzzle, it read the full body without resetting the pointer after. Using rewind() will reset the pointer. If this is the source of your issue rewind() will resolve this. It has aleady been resolved in newer versions.

Related

Why I failed to catch ModelNotFoundException ths error in controller block?

In laravel 9 I have ProductCardReport component which is on blade form and data are retrieved
from ReportProduct class. I got data with firstOrFail in this class and I failed to catch this
error in controller block. I have in ProductController controller :
public function showReport(string $productId)
{
try {
return view('admin/products/report', [
'productId' => $productId,
]);
} catch (ModelNotFoundException $e) { // I EXPECTED THIS BLOCK WOULD WORK
\Log::info('-1ModelNotFoundException:');
return redirect(route('admin.products.edit', $this->productId))
->with('message', 'Product "' . $this->productId . '" not found.')
->with('message_type', 'error');
} catch (Exception $e) {
\Log::info('-1 ProductCardReport -2 showReport $e->getMessage() ::' . print_r($e->getMessage(), true));
return back()->withErrors(['message' => $e->getMessage()]);
}
}
In view 'admin/products/report' I set component :
<x-app-layout>
<x-product-card-report product-id="{{ $productId }}" />
</x-app-layout>
and in component app/View/Components/ProductCardReport.php :
public function render()
{
$reportProduct = new ReportProduct();
$itemRetrieved = $reportProduct->retrieveItem( id : $this->productId );
...
}
And in app/Library/ReportProduct.php I get data with invalid ID :
class ReportProduct implements ReportRetrieveItemInterface, ReportDownloadItemInterface
{
public function __construct()
{
$uploadedFileManagement= app(UploadedFileManagementInterface::class);
$this->uploadedFileManagement = $uploadedFileManagement;
}
public function retrieveItem(string $id, array $getRelatedData = []) : bool
{
$this->product = Product
::getById($id .'ERROR') // ModelNotFoundException is raised here.
->firstOrFail();
But I got uncaught error :
Illuminate\Database\Eloquent\ModelNotFoundException
No query results for model [App\Models\Product].
and try block in ProductController did not work.
How it can be fixed ?
Thanks!

how to add exception in laravel migration?

I'm doing a migration and I want the execution of "artisan migrate" to stop until a field does not have the value 'CONTACT_INFO' inside.
What I want to do is an exception when I detect that this value is not there.
public function up()
{
$emailConfiguration = EConfig::where('clave','CONTACTO_NAME')->get()->first();
$eConfig = EConfig::find($emailConfiguration->pages_id);
$Languages = Language::all();
foreach ($Languages as $key => $lang) {
$exists = !is_null($eConfig->pages_id);
if ($exists) {
$value = $eConfig->detail()->where('language_id', $lang->languages_id)->first()->pages_valor;
if (strpos($value,'CONTACTO_INFO') == false) {
InvalidOrderException::reportable(function (InvalidOrderException $e) {
echo 'error';
});
}
}
}
}
If I'm understanding your question correctly, you want to stop the migrations if a certain condition is not satisfied.
To do so, you need to throw the exception you want, for instance, with your code :
public function up()
{
//...
foreach ($Languages as $key => $lang) {
$exists = !is_null($eConfig->pages_id);
if ($exists) {
// ...
if (strpos($value,'CONTACTO_INFO') == false) {
throw new \RuntimeException('Migration stopped due to invalid data');
}
}
}
}
However you may want to wrap it in a transaction to avoid any undesired behaviors
use Illuminate\Support\Facades\DB;
public function up()
{
DB::transaction(function(){
//...
foreach ($Languages as $key => $lang) {
$exists = !is_null($eConfig->pages_id);
if ($exists) {
// ...
if (strpos($value,'CONTACTO_INFO') == false) {
throw new \RuntimeException('Migration stopped due to invalid data');
}
}
}
}
});
If you want to customize your exception, you can create your own with php artisan make:exception MyMigrationCustomException

Laravel Create a request internally Resolved

I need to recreate a resquest so that it behaves like a call via api to go through the validator, but my $request->input('rps.number') always arrives empty, although I can see the data in the debug
I also couldn't get it to go through the laravel validator
I can't use a technique to make an http call, because I need to put this call in a transaction
<?php
$nota = new stdClass();
$rps = new stdClass();
$rps->numero = (int)$xml->Rps->IdentificacaoRps->Numero;
$rps->serie = (string)$xml->Rps->IdentificacaoRps->Serie;
$rps->tipo = (int)$xml->Rps->IdentificacaoRps->Tipo;
$nota->rps = $rps;
$controller = new NotaController(new Nota());
$content = new StoreNotaRequest();
$content->request->add($nota);
$result = $controller->store($content);
StoreNotaRequest
<?php
class StoreNotaRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$request = $this->request;
return [
'rps.numero' => 'required_with:rps|numeric|between:1,999999999999999',
'rps.serie' => 'required_with:rps|string|min:1|max:5',
'rps.tipo' => 'required_with:rps|integer|in:1,2,3'
];
}
}
NotaController
<?php
class NotaController extends Controller
{
private Nota $nota;
public function __construct(Nota $nota)
{
$this->nota = $nota;
}
public function store(StoreNotaRequest $request): JsonResponse
{
// $validated = $request->validated();
try {
$nota = DB::transaction(function () use ($request) {
$request->input('rps.numero');
});
return response()->json($nota);
} catch (Throwable $e) {
return response()->json($data, 409);
}
}
}
Solution
the solution was a little too verbose, I believe it is possible to solve with less code.
more does what it needs to go through the validation of the data contained in the StoreNotaRequest
and it returns an http response, in addition to being able to put all these isolated calls in a single transaction
DB::beginTransaction();
$errors = [];
foreach ($itens as $item) {
$controller = new NotaController(new Nota());
$request = new StoreNotaRequest();
$request->setMethod('POST');
$request->request->add($nota);
$request
->setContainer(app())
->setRedirector(app(Redirector::class))
->validateResolved();
$response = $controller->store($request);
if ($response->statusText() !== 'OK') {
$errors[$item->id] = 'ERROR';
}
}
if (count($errors) === 0) {
DB::commit();
} else {
DB::rollBack();
}

Google Client API setAccessToken() before isAccessTokenExpired() results in invalid credentials

I am working with the Google Client API in Laravel to allow my users to sync their calendars with Google. Everything works, but the issue I am running into is when their tokens expire they are getting an "Invalid Credentials" error, in order to fix it they have to log out and log back in which I am trying to avoid.
I don't understand why setAccessToken() is to be called before isAccessTokenExpired().
I need to check if the access token is expired before I set it but if I do it this way then isAccessTokenExpired() always returns true.
Any ideas would be helpful. Thanks!
Here is my code:
GoogeServiceController.php
class GoogleServiceController extends Controller
{
protected $client;
protected $service;
public function __construct()
{
$client = new Google_Client();
$client->setAuthConfig(Config::get('google_config.web'));
$client->setAccessType('offline');
$client->addScope(Google_Service_Calendar::CALENDAR);
$service = new Google_Service_Calendar($client);
$this->client = $client;
$this->service = $service;
}
public function oauth(Request $request)
{
if (App::environment('local')) {
$this->client->setRedirectUri('http://esm.development.com/oauth');
} else {
$this->client->setRedirectUri('https://essentialstudiomanager.com/oauth');
}
if (is_null($request->user()->refresh_token)) {
$this->client->setApprovalPrompt("force");
}
if (!$request->has('code')) {
$auth_url = $this->client->createAuthUrl();
$filtered_url = filter_var($auth_url, FILTER_SANITIZE_URL);
return redirect($filtered_url);
} else {
$this->client->authenticate($request->code);
if (is_null($request->user()->refresh_token)) {
$refresh_token = $this->client->getRefreshToken();
$user = $request->user();
$user->refresh_token = $refresh_token;
$user->save();
}
$request->session()->put('access_token', $this->client->getAccessToken());
$notification = ['message' => 'Your calendar is now synced with your Google Calendar.', 'alert-type' => 'success'];
return redirect()->route('home')->with($notification);
}
}
}
GoogleEventController.php
public function updateGoogleEvent($request, $event, $title, $description, $start, $end)
{
if ($request->session()->has('access_token')) {
$this->client->setAccessToken(session('access_token'));
if ($this->client->isAccessTokenExpired()) {
$this->client->refreshToken($request->user()->refresh_token);
$request->session()->put('access_token', $this->client->getAccessToken());
$this->client->setAccessToken(session('access_token'));
}
} else {
return redirect()->route('oauthCallBack');
}
$users_calendar = $this->service->calendars->get('primary');
$get_event = $this->service->events->get('primary', $event->google_event_id);
$get_event->setSummary($title);
$get_event->setDescription($description);
$start_date = new Google_Service_Calendar_EventDateTime();
$start_date->setDateTime($start);
$start_date->setTimeZone($users_calendar->timeZone);
$get_event->setStart($start_date);
$end_date = new Google_Service_Calendar_EventDateTime();
$end_date->setDateTime($end);
$end_date->setTimeZone($users_calendar->timeZone);
$get_event->setEnd($end_date);
$updatedEvent = $this->service->events->update('primary', $get_event->getId(), $get_event);
}

How to produce API error responses in Laravel 5.4?

Whenever I make a call to /api/v1/posts/1, the call is forwarded to the show method
public function show(Post $post) {
return $post;
}
in PostController.php resourceful controller. If the post does exist, the server returns a JSON response. However, if the post does not exist, the server returns plain HTML, despite the request clearly expecting JSON in return. Here's a demonstration with Postman.
The problem is that an API is supposed to return application/json, not text/html. So, here are my questions:
1. Does Laravel have built-in support for automatically returning JSON if exceptions occur when we use implicit route model binding (like in show method above, when we have 404)?
2. If it does, how do I enable it? (by default, I get plain HTML, not JSON)
If it doesn't what's the alternative to replicating the following across every single API controller
public function show($id) {
$post = Post::find($id); // findOrFail() won't return JSON, only plain HTML
if (!$post)
return response()->json([ ... ], 404);
return $post;
}
3. Is there a generic approach to use in app\Exceptions\Handler?
4. What does a standard error/exception response contain? I googled this but found many custom variations.
5. And why isn't JSON response still built into implicit route model binding? Why not simplify devs life and handle this lower-level fuss automatically?
EDIT
I am left with a conundrum after the folks at Laravel IRC advised me to leave the error responses alone, arguing that standard HTTP exceptions are rendered as HTML by default, and the system that consumes the API should handle 404s without looking at the body. I hope more people will join the discussion, and I wonder how you guys will respond.
I use this code in app/Exceptions/Handler.php, probably you will need making some changes
public function render($request, Exception $exception)
{
$exception = $this->prepareException($exception);
if ($exception instanceof \Illuminate\Http\Exception\HttpResponseException) {
return $exception->getResponse();
}
if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
return $this->unauthenticated($request, $exception);
}
if ($exception instanceof \Illuminate\Validation\ValidationException) {
return $this->convertValidationExceptionToResponse($exception, $request);
}
$response = [];
$statusCode = 500;
if (method_exists($exception, 'getStatusCode')) {
$statusCode = $exception->getStatusCode();
}
switch ($statusCode) {
case 404:
$response['error'] = 'Not Found';
break;
case 403:
$response['error'] = 'Forbidden';
break;
default:
$response['error'] = $exception->getMessage();
break;
}
if (config('app.debug')) {
$response['trace'] = $exception->getTrace();
$response['code'] = $exception->getCode();
}
return response()->json($response, $statusCode);
}
Additionally, if you will use formRequest validations, you need override the method response, or you will be redirected and it may cause some errors.
use Illuminate\Http\JsonResponse;
...
public function response(array $errors)
{
// This will always return JSON object error messages
return new JsonResponse($errors, 422);
}
Is there a generic approach to use in app\Exceptions\Handler?
You can check if json is expected in the generic exception handler.
// app/Exceptions/Handler.php
public function render($request, Exception $exception) {
if ($request->expectsJson()) {
return response()->json(["message" => $exception->getMessage()]);
}
return parent::render($request, $exception);
}
The way we have handled it by creating a base controller which takes care of the returning response part. Looks something like this,
class BaseApiController extends Controller
{
private $responseStatus = [
'status' => [
'isSuccess' => true,
'statusCode' => 200,
'message' => '',
]
];
// Setter method for the response status
public function setResponseStatus(bool $isSuccess = true, int $statusCode = 200, string $message = '')
{
$this->responseStatus['status']['isSuccess'] = $isSuccess;
$this->responseStatus['status']['statusCode'] = $statusCode;
$this->responseStatus['status']['message'] = $message;
}
// Returns the response with only status key
public function sendResponseStatus($isSuccess = true, $statusCode = 200, $message = '')
{
$this->responseStatus['status']['isSuccess'] = $isSuccess;
$this->responseStatus['status']['statusCode'] = $statusCode;
$this->responseStatus['status']['message'] = $message;
$json = $this->responseStatus;
return response()->json($json, $this->responseStatus['status']['statusCode']);
}
// If you have additional data to send in the response
public function sendResponseData($data)
{
$tdata = $this->dataTransformer($data);
if(!empty($this->meta)) $tdata['meta'] = $this->meta;
$json = [
'status' => $this->responseStatus['status'],
'data' => $tdata,
];
return response()->json($json, $this->responseStatus['status']['statusCode']);
}
}
Now you need to extend this in your controller
class PostController extends BaseApiController {
public function show($id) {
$post = \App\Post::find($id);
if(!$post) {
return $this->sendResponseStatus(false, 404, 'Post not found');
}
$this->setResponseStatus(true, 200, 'Your post');
return $this->sendResponseData(['post' => $post]);
}
}
You would get response like this
{
"status": {
"isSuccess": false,
"statusCode": 404,
"message": "Post not found"
}
}
{
"status": {
"isSuccess": true,
"statusCode": 200,
"message": "Your post"
},
"data": {
"post": {
//Your post data
}
}
}
You just use use Illuminate\Support\Facades\Response;.
then, make the return as am:
public function index(){
$analysis = Analysis::all();
if(empty($analysis)) return Response::json(['error'=>'Empty data'], 200);
return Response::json($analysis, 200, [], JSON_NUMERIC_CHECK);
}
And now you will have a JSON return....

Resources