Add status flag to Laravel validation response - laravel

How can I add the status flag to Laravel (6.0) validation response?
this is my validation class.
class LoginRequest extends FormRequest{
public function authorize()
{
return true;
}
public function rules()
{
return [
'email' => 'required|email|exists:users,email',
'password' => 'required|min:4|max:8'
];
}
}
according to the above validation following response is a return
{
"message": "The given data was invalid.",
"errors": {
"email": [
"The selected email is invalid."
]
}
}
but I need reformat above response like this.
{
"status": "fail",
"message": "The given data was invalid.",
"errors": {
"email": [
"The selected email is invalid."
]
}
}

All you need, create your response method and override failedValidation. Clear?
Update
In LoginRequest
protected function failedValidation(Validator $validator)
{
$this->currentValidator = $validator;
throw new ValidationException($validator, $this->errorResponse(
$this->formatErrors($validator)
));
}
protected function errorResponse(array $errors)
{
//something else
return response($errors);
}
Please look up Illuminate\Validation\ValidationException

Create a custom exception.
php artisan make:exception MyCustomException
Then, override the failedValidation method from FormRequest class
use Illuminate\Contracts\Validation\Validator;
use App\Exceptions\MyCustomException;
class LoginRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'email' => 'required|email|exists:users,email',
'password' => 'required|min:4|max:8'
];
}
/**
* Handle a failed validation attempt.
*
* #param \Illuminate\Contracts\Validation\Validator $validator
*
* #return void
*
* #throws \App\Exceptions\MyCustomException
*/
protected function failedValidation(Validator $validator)
{
throw (new MyCustomException($validator));
}
}
and handle the response in App\Exceptions\MyCustomException class
<?php
namespace App\Exceptions;
use Exception;
use Symfony\Component\HttpFoundation\Response;
class MyCustomException extends Exception
{
/**
* The validator instance.
*
* #var \Illuminate\Contracts\Validation\Validator
*/
public $validator;
/**
* Create a new exception instance.
*
* #param \Illuminate\Contracts\Validation\Validator $validator
* #return void
*/
public function __construct($validator)
{
parent::__construct('The given data was invalid.');
$this->validator = $validator;
}
/**
* Get all of the validation error messages.
*
* #return array
*/
public function errors()
{
return $this->validator->errors()->messages();
}
/**
* Report the exception.
*
* #return void
*/
public function report()
{
//
}
/**
* Render the exception into an HTTP response.
*
* #param \Illuminate\Http\Request
* #return \Illuminate\Http\Response
*/
public function render($request)
{
if ($request->acceptsJson()) {
$errors = [
'status' => false,
'message' => 'The given data was invalid',
'errors' => $this->errors(),
];
return response()->json($errors, Response::HTTP_UNPROCESSABLE_ENTITY);
}
}
}

Related

How to get error validation data in Custom Form Request

I have created a custom request form for login validation, It works fine if I put email and password field that's what I set for rules, email and password is required, but the problem is, I can't use the errors data,
/**
* Signin Controller
*
* #param \App\Http\Requests\Auth\Signin $request
*/
public function signin(Signin $request)
{
if ($request->validated()) {
return response()->json(['hello' => 'hi']);
} else {
return response()->json(['error' => $request->errors()]);
}
}
This is the Request form
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Foundation\Http\FormRequest;
class Signin extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array<string, mixed>
*/
public function messages()
{
return [
'email.required' => 'Email field is required.'
];
}
/**
* Get the validation rules that apply to the request.
*
* #return array<string, mixed>
*/
public function rules()
{
return [
'email' => 'required',
'password' => 'required'
];
}
}
Now when I try to make a post request to the signin controller, if email and password included then its return a json response, but if I only use email or password then it response a 405 method not allowed I'm expecting a json response with error data like errors()
As I understood, failedValidation would help you
use Illuminate\Http\JsonResponse;
class Signin extends FormRequest
{
public function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json($validator->errors(), JsonResponse::HTTP_UNPROCESSABLE_ENTITY));
}
}
This return the JSON object with 422 error(default error code for validation)

Am I overcomplicating events/listeners/notifications?

I'm not sure if I'm overcomplicating events/listeners/notifications in Laravel, but it feels like maybe I am, and I'm getting some unexpected results, although technically it's working.
My basic structure is this:
MonthlySummaryCompletedEventis fired.
The HandleMonthlySummaryCompletedEvent listener listens for the event.
It triggers a SendMonthlySummaryCreatedNotification notification.
This is all working (locally at least), except for 2 things:
I've had to put in a 5 second sleep in my livewire component that receives the notification because of timing issues. It almost seems like the pusher notification arrives before the notification has been written to the database.
It appears that 2 messages are sent to pusher:
Here's the code for my event:
class MonthlySummaryCompletedEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public User $requestedBy;
public string $fileDownloadUrl;
public string $fileName;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($event, string $fileDownloadUrl, string $fileName)
{
$this->requestedBy = $event->requestedBy;
$this->fileDownloadUrl = $fileDownloadUrl;
$this->fileName = $fileName;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('users.' . $this->requestedBy->id);
}
}
here's the code for my listener:
class HandleMonthlySummaryCompletedEvent implements ShouldQueue
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param object $event
* #return void
*/
public function handle($event)
{
Notification::send($event->requestedBy, new SendMonthlySummaryCreatedNotification($event));
}
}
and here's the code for the notification:
class SendMonthlySummaryCreatedNotification extends Notification implements ShouldQueue, ShouldBroadcast
{
use Queueable;
public $fileDownloadUrl;
public $fileName;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($event)
{
$this->fileDownloadUrl = $event->fileDownloadUrl;
$this->fileName = $event->fileName;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database', 'broadcast'];
}
/**
* Get the broadcastable representation of the notification.
*
* #param mixed $notifiable
* #return BroadcastMessage
*/
public function toBroadcast($notifiable)
{
Log::debug($notifiable->toArray());
return new BroadcastMessage([
'title' => 'Monthly Summary Complete',
'message' => "{$this->fileName} is ready. ",
'link' => $this->fileDownloadUrl,
'link_text' => 'Click here to download',
'show_toast' => true,
'user_id' => $notifiable->id
]);
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toDatabase($notifiable)
{
return [
'title' => 'Monthly Summary Complete',
'message' => "{$this->fileName} is ready. ",
'link' => $this->fileDownloadUrl,
'link_text' => 'Click here to download',
'show_toast' => true,
'user_id' => $notifiable->id
];
}
}
And here's my front end livewire component:
public function getListeners()
{
return [
"echo-private:users.{$this->user->id},StatementCompleted" => 'notifyUser',
"echo-private:users.{$this->user->id},MonthlySummaryCompletedEvent" => 'notifyUser',
];
}
public function mount()
{
$this->user = Auth::user();
$this->refreshNotifications();
$this->showNotificationsBadge = ($this->notificationCount > 0) ? true : false;
}
public function render()
{
return view('livewire.components.notifications');
}
public function notifyUser()
{
$this->showNotificationsBadge = true;
sleep(5);
$this->refreshNotifications();
$message = $this->notifications->first()->data['message'];
if (isset($this->notifications->first()->data['show_toast']) && $this->notifications->first()->data['show_toast'] == true) {
$this->dispatchBrowserEvent('triggerToast', [Helper::notification('Success', $message)]);
}
}

Problem with overriding Laravel MessageBag

My goal:
We ar developing an API and we need to customize error message not only to send custom string, but also to send cusotm code.
E.g.: custom invalid email error message should look like this:
error[
'code' => 102,
'message' => 'invalid email'
]
I could set these custom error messages, to be arrays, but I have a problem with emails.
I get:
"Array to string conversion" at Illuminate\Support\MessageBag at line 248.
The reason of it, is because it is expecting a string and now I have an array.
protected function transform($messages, $format, $messageKey)
{
return collect((array) $messages)
->map(function ($message) use ($format, $messageKey) {
// We will simply spin through the given messages and transform each one
// replacing the :message place holder with the real message allowing
// the messages to be easily formatted to each developer's desires.
return str_replace([':message', ':key'], [$message, $messageKey], $format);
})->all();
}
I would like to override (bind) this method with:
protected function transform($messages, $format, $messageKey)
{
return collect((array) $messages)
->map(function ($message) use ($format, $messageKey) {
if(is_array($message)){
$message = json_encode($message);
}
// We will simply spin through the given messages and transform each one
// replacing the :message place holder with the real message allowing
// the messages to be easily formatted to each developer's desires.
return str_replace([':message', ':key'], [$message, $messageKey], $format);
})->all();
}
I have daone the following steps.
I have created Libraries/Extensions/MessagesBag folder and plced the followint files there.
MessageBagServiceProvider.php
namespace App\Libraries\Extensions\MessageBag;
use Illuminate\Support\ServiceProvider;
class MessageBagServiceProvider extends ServiceProvider{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app->bind('Illuminate\Support\MessageBag', 'App\Libraries\Extensions\MessageBag\YcoMessageBag');
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return array('messagebag');
}
}
MessageBagFacade.php
namespace App\Libraries\Extensions\MessageBag;
use Illuminate\Support\Facades\Facade as IlluminateFacade;
class MessageBagFacade extends IlluminateFacade {
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor() { return 'messagebag'; }
}
YcoMessagebag.php
namespace App\Libraries\Extensions\MessageBag;
use Illuminate\Support\MessageBag as OriginalMessageBag;
class YcoMessageBag extends OriginalMessageBag{
/**
* Format an array of messages.
*
* #param array $messages
* #param string $format
* #param string $messageKey
* #return array
*/
protected function transform($messages, $format, $messageKey)
{
return collect((array) $messages)
->map(function ($message) use ($format, $messageKey) {
if(is_array($message)){
$message = json_encode($message);
}
// We will simply spin through the given messages and transform each one
// replacing the :message place holder with the real message allowing
// the messages to be easily formatted to each developer's desires.
return str_replace([':message', ':key'], [$message, $messageKey], $format);
})->all();
}
}
I have registered my MessageBagServiceprodider.php in config/app.php
App\Libraries\Extensions\MessageBag\MessageBagServiceProvider::class,
When I have died and dumped in MessageBagServiceProvider's register method, it worked, the code died and dumped "hello".
But the MessageBag class is not overriding, still the original class is loaded.
I have tried to play with
$this->app->bind('Illuminate\Support\MessageBag', 'App\Libraries\Extensions\MessageBag\YcoMessageBag');
I have tried with: $this->app->singleton,
I have tried to reach the original class with \Illuminate\Support\MessageBag also tried with '\App\Libraries\Extensions\MessageBag\YcoMessageBag', but no success.
Can I override this class? What can be the solution?
Thank you!
I have figured it out, here is my solution:
I have created Exteptions/Handler.php
class Handler extends ExceptionHandler
{
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $e
*
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if ($exception instanceof ValidationException) {
return $this->convertValidationExceptionToResponse($exception, $request);
} elseif ($exception instanceof ModelNotFoundException) {
$modelName = strtolower(class_basename($exception->getModel()));
return $this->errorResponse(
'Does not exists any ' . $modelName . ' with this id',
Response::HTTP_NOT_FOUND
);
} elseif ($exception instanceof AuthenticationException) {
return $this->unauthenticated($request, $exception);
} elseif ($exception instanceof AuthorizationException) {
return $this->errorResponse($exception->getMessage(), Response::HTTP_FORBIDDEN);
} elseif ($exception instanceof MethodNotAllowedHttpException) {
return $this->errorResponse('The specified request is invalid!', Response::HTTP_METHOD_NOT_ALLOWED);
} elseif ($exception instanceof NotFoundHttpException) {
return $this->errorResponse('The specified url cannot be found!', Response::HTTP_NOT_FOUND);
} elseif ($exception instanceof HttpException) {
$message = $exception->getMessage();
$status = $exception->getStatusCode();
$httpStatusCodes = collect(Response::$statusTexts);
if (!$httpStatusCodes->has($status)) {
$status = Response::HTTP_UNPROCESSABLE_ENTITY;
}
if ($message == "") {
$message = "An error occured when processing request!";
}
return $this->errorResponse($message, $status);
}
return parent::render($request, $exception);
}
/**
* Convert an authentication exception into a response.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Auth\AuthenticationException $exception
*
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function unauthenticated($request, AuthenticationException $exception)
{
return $this->errorResponse('Unauthenticated', Response::HTTP_UNAUTHORIZED);
}
/**
* Create a response object from the given validation exception.
*
* #param \Illuminate\Validation\ValidationException $e
* #param \Illuminate\Http\Request $request
*
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function convertValidationExceptionToResponse(ValidationException $e, $request)
{
$errors = $e->validator->errors()->getMessages();
$errors = $this->convertValidationErrors($errors);
return response()->json([
'success' => false,
'error' => $errors,
'status' => 422,
'message' => $e->getMessage()
], 422);
}
protected function convertValidationErrors($errors){
$codes = [
101 => [
'code'=> 101,
'message'=> 'A mező kitöltése kötelező'
],
106=>[
'code'=> 106,
'message'=> 'A mező tartalma nem elég hosszú'
],
102 =>[
'code'=> 102,
'message'=> 'A mező tartalma nem elég hosszú'
],
103 => [
'code'=> 103,
'message'=> 'A mező tartalma túl hosszú'
],
105 => [
'code'=> 105,
'message'=> ' A mező tartalma nem szöveg'
],
107 => [
'code'=> 107,
'message'=> 'Formátum nem megfelelő'
],
104 => [
'code'=> 104,
'message'=> 'Az email formátuma nem megfelelő'
],
];
foreach($errors as $key => $code){
if(isset($codes[$code[0]])) {
unset($errors[$key][0]);
$errors[$key]['code'] = $codes[$code[0]]['code'];
if(env('APP_ENV') != 'local') {
$errors[$key]['message'] = $codes[$code[0]]['message'];
}
}
}
return $errors;
}
}
Maybe not the most beautiful solution, but it works.

Unable to overwrite authorized method in policy

I want to respond with a custom message when authorization fails.
I've overwritten the method in the Policy class but it does not return the custom message.
Policy:
class PostPolicy
{
use HandlesAuthorization;
/**
* Determine if user can view post
* #param User $user
* #param Post $post
* #return bool
*/
public function view(User $user, Post $post)
{
return $user
->posts()
->where('post_id', $post->id)
->exists();
}
/**
* [deny description]
* #return [type] [description]
*/
protected function deny()
{
return response()->json([
'message' => 'My custom unauthorized message'
], 401);
}
}
Implementing in PostController:
...
public function show(Post $post)
{
$this->authorize('view', $post);
...
}
The response still returns whats defined in the HandlesAuthorization trait, i.e.:
protected function deny($message = 'This action is unauthorized.')
{
throw new AuthorizationException($message);
}
You can simply add this code inside the AuthorizationException.php
/**
* Render the exception into an HTTP response.
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function render(Request $request)
{
if ($request->is('api/*')) {
$response = [
'message' => $this->message,
'status' => 403,
];
return response()->json($response, 403);
}
}

Trying to get property of non-object. Error in laravel

I'm kinda new at laravel and need your help.
I have this IR model :
class IR extends Model
{
//
protected $fillable=['irnum','date','subject','cause','facts','as','at','rec','user_id'];
protected $casts=['user_id'=>'int'];
public function user()
{
return $this->belongsTo(user::class);
}
public static $rules =array (
'date'=>'required',
'status'=>'required|min:10',
'cause' => 'required|min:10',
'facts' => 'required|min:10',
'ir-as' => 'required|min:10',
'rec' => 'required|min:10',
'ir-at' => 'required|min:10',
);
}
and route:
Route::group(['middleware' => ['web']], function () {
Route::get('/', function () {
return view('welcome');
})->middleware('guest');
Route::resource('tasks','TaskController');
Route::get('ir',function ()
{
return View::make('tasks/ir');
});
Route::resource('irs','IRController');
Route::auth();
});
and this is my controller :
class IRController extends Controller
{
/**
* The task repository instance.
*
* #var TaskRepository
*/
protected $irs;
/**
* Create a new controller instance.
*
* #param TaskRepository $tasks
* #return void
*/
public function __construct(IRRepository $irs)
{
$this->middleware('auth');
$this->irs = $irs;
}
/**
* Display a list of all of the user's task.
*
* #param Request $request
* #return Response
*/
public function index(Request $request)
{
return view('tasks.ir',[
'irs' => $this->irs->forUser($request->user()),
]);
}
/**
* Create a new task.
*
* #param Request $request
* #return Response
*/
public function create()
{
return View::make('irs.create');
}
public function store(Request $request)
{
$request->user_id=Auth::user()->id;
$input =$request->all();
$validation=Validator::make($input, IR::$rules);
if($validation->passes())
{
IR::create($input);
return Redirect::route('tasks.ir');
}
return Redirect::route('tasks.ir')
->withInput()
->withErrors($validation)
->with('message','There were validation errors.');
}
/**
* Destroy the given task.
*
* #param Request $request
* #param Task $task
* #return Response
*/
public function destroy(Request $request, IR $irs)
{
}
}
I really dont know what causes to throw this error.
Error throws when i add Incident report.
Pls help.
New at laravel
You're saying you get an error when you're trying to add an incident report or IR, so I assume problem is in a store() action.
I can see only one potential candidate for this error in a store() action:
Auth::user()->id;
Add dd(Auth::user()); before this clause and if it will output null, use check() method, which checks if any user is authenticated:
if (Auth::check()) {
Auth::user->id;
}

Resources