As we know, we can define every RestApi in Laravel and we can implementing them into Route or Controllers, this below code is one of my simple RestApi which i want to convert that to GraphQl
public function loginAccount(RequestLoginAccount $request): JsonResponse
{
$user = User::where('mobile_number', $request->input('mobile_number'))->first();
if (!$user) {
return response()->json(['response' => false]);
}
try {
$user->notify(new LoginVerifyCode($user->mobile_number));
} catch (Exception $exception) {
return response()->json(['response' => -1]);
}
return response()->json(['response' => true]);
}
after creating simple resolver i have this query:
command:
php artisan lighthouse:query loginAccount
loginAccount query:
type Query {
///
loginAccount(mobile_number:String!, verify_code: String!): User
}
loginAccount resolver:
class LoginAccount
{
public function __invoke($_, array $args)
{
$user = User::where('mobile_number', $args['mobile_number'])->first();
if (!$user) {
return null;
}
/*try {
$user->notify(new LoginVerifyCode($user->mobile_number));
} catch (Exception $exception) {
return response()->json(['response' => -1]);
}
return response()->json(['response' => true]);*/
}
}
now could you help me to know whats equivalent of this RestApi in GraphQl? or is any custom response to define them?
I found how can i convert it from RestApi to GraphQl and it's resolved
i should be have custom type in schema:
type LoginResponce{
response:Int
}
custom GraphQl resolve should be has:
class LoginAccount
{
public function __invoke($_, array $args): array
{
$user = User::where('mobile_number', $args['mobile_number'])->first();
if (!$user) {
return ['response' => -1];
}
try {
$user->notify(new LoginVerifyCode($user->mobile_number));
} catch (Exception $exception) {
return ['response' => -2];
}
return ['response' => 1];
}
}
and query:
type Query {
//
loginAccount(mobile_number:String!): LoginResponce
}
GraphQl query:
query {
loginAccount(mobile_number: "00000") {
response
}
}
Related
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);
}
}
}
Im trying intercept errors in laravel and i found a nice way to do that:
Simulating a error:
public function index(){
$users = User::all(); //<-SQL exeception here
return response()->json(['message'=>'ok'], 200);
}
app/Exceptions/Handler.php
public function report(Exception $exception)
{
dd($exception); //<-intercept my error here
parent::report($exception);
}
Works very well and i can do whatever i want with error, but when i use a try-catch block, my interceptor does not work:
Simulating error again
public function index(){
try {
$users = User::all();//<-SQL exeception here
} catch (\Throwable $th) {
error_log('Error handled');
//MyInterceptor::manuallyIntercept($th);
}
return response()->json(['message'=>'ok'], 200);
}
Is there a clean way to intercept all handled errors programatically?
Not report method, you need to use render method on Handler.php
You will see $this->errorResponse which is to just return JSON response. I just want to show the main idea.
public function render($request, Exception $exception)
{
if ($exception instanceof ValidationException) {
return $this->convertValidationExceptionToResponse($exception, $request);
}
if ($exception instanceof ModelNotFoundException) {
$modelName = strtolower(class_basename($exception->getModel()));
return $this->errorResponse("Does not exists any {$modelName} with the specified identificator", 404);
}
if ($exception instanceof AuthenticationException) {
return $this->unauthenticated($request, $exception);
}
if ($exception instanceof AuthorizationException) {
return $this->errorResponse($exception->getMessage(), 403);
}
if ($exception instanceof MethodNotAllowedHttpException) {
return $this->errorResponse('The specified method for the request is invalid', 405);
}
if ($exception instanceof NotFoundHttpException) {
return $this->errorResponse('The specified URL cannot be found', 404);
}
if ($exception instanceof HttpException) {
return $this->errorResponse($exception->getMessage(), $exception->getStatusCode());
}
if ($exception instanceof QueryException) {
$errorCode = $exception->errorInfo[1];
if ($errorCode == 1451) {
return $this->errorResponse('Cannot remove this resource permanently. It is related with any other resource', 409);
}
}
if (config('app.debug')) {
return parent::render($request, $exception);
}
return $this->errorResponse('Unexpected Exception. Try later', 500);
}
Error response method
protected function errorResponse($message, $code)
{
return response()->json(['error' => $message, 'code' => $code], $code);
}
As I was working on my Laravel app, I noticed that I was creating controllers' methods which were very similar. For example, I have models: Task, Block, Lesson, etc. And in every controller there are very similar methods:
public function index()
{
return new GeneralResource(/model/::all());
}
public function show(/model/ $model)
{
return new GeneralResource($model);
}
public function store(/model/Request $request)
{
try {
$model = /model/::create($request->validated());
return response()->json(['status' => 'success']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function update(/model/Request $request, /model/ $model)
{
try {
$model->update($request->validated());
return response()->json(['status' => 'success']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function destroy(/model/ $model)
{
try {
$model->delete();
return response()->json(['status' => 'success']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
Maybe I could extend Controller class with these methods and pass parameters as protected properties, but I am not sure how this can be done.
Please, help.
What you are looking for is called Template Method. You can do it for each method from the controllers :)
More info:
https://refactoring.guru/design-patterns/template-method/php/example
https://designpatternsphp.readthedocs.io/en/latest/Behavioral/TemplateMethod/README.html
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'm making a Laravel 5.4 project where I want users to select a school (which they will be assigned to) before they register and the user is created.
My problem is that a google login is the main login method of the application and the user creation is handled in the LoginController. I'm using the Socialite package for the google login.
How do I pass the user and the data from the google callback to the register page?
This is my LoginController:
public function redirectToProvider()
{
return Socialite::driver('google')->redirect();
}
public function handleProviderCallback()
{
try
{
$user = Socialite::driver('google')->user();
}
catch (Exception $e)
{
return redirect('auth/google');
}
$authUser = $this->findOrCreateUser($user);
Auth::login($authUser, true);
return redirect('/home');
}
public function findOrCreateUser($googleUser)
{
$authUser = User::where('google_id', $googleUser->id)->first();
if ($authUser) {
return $authUser;
}
return User::create([
'name' => $googleUser->name,
'email' => $googleUser->email,
'google_id' => $googleUser->id,
'avatar' => $googleUser->avatar
]);
}
You can use flashed session.
Try this:
public function handleProviderCallback()
{
try
{
$user = Socialite::driver('google')->user();
}
catch (Exception $e)
{
return redirect('auth/google');
}
$authUser = User::where('google_id', $user->id)->first();
if($authUser) //if user is found
{
Auth::login($authUser, true);
return redirect('/home');
}
else //if user is not found, redirect to register page
{
return redirect('/register')->with('user', $user);
}
}
and in your register view
<input type='text' name='email' value='{{session("user")->email}}'>