How to make validation response as json when using request class - laravel

I have these request file to register a new customer, I want to be the response json when the validation fails, how can I do that?
This is the request class
<?php
namespace App\Http\Requests\Validations;
use App\Http\Requests\Request;
class RegisterCustomerRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|min:3|max:255',
'email' => 'required|email|max:255|unique:customers',
'password' => 'required|string|min:6|confirmed',
'agree' => 'required',
];
}
}
And the registration function:
public function register(RegisterCustomerRequest $request)
{
$customer = Customer::create([
'name' => $request->name,
'email' => $request->email,
'password' => $request->password,
'accepts_marketing' => $request->subscribe,
'verification_token' => Str::random(40),
'active' => 0,
]);
// Sent email address verification notich to customer
$customer->notify(new EmailVerificationNotification($customer));
$customer->generateToken();
event(new Registered($customer));
return new CustomerResource($customer);
}

in your RegisterCustomerRequest you can override default method failedValidation as below
protected function failedValidation(Validator $validator)
{
if ($this->ajax()){
throw new HttpResponseException($this->respondWithError(419,'VALIDATION_ERROR',$validator->errors()));
} else{
throw (new ValidationException($validator))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
}
also you can write your own response inside $this->ajax() like below
if ($this->ajax()){
return response()->json($validator->errors(),419);
}

Related

Why Laravel validation returns always the default message?

When I send wrong data to server using AJAX, Laravel checkes it and throw message error:
The given data was invalid.
My requests validation is:
class RegistrationRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function messages()
{
return [
'email' => 'required|email|unique:users',
'password' => 'required|min:6',
];
}
public function rules()
{
return [
'email' => 'required|email|unique:users',
'password' => 'required|min:6',
];
}
}
Using is:
public function register(RegistrationRequest $request){}
Handler exceptions is:
public function render($request, Exception $exception)
{
if ($exception instanceof ValidationException) {
return response()->json(['type' => 'error',
'codes' => [$exception->getMessage()]], 400);
}
return parent::render($request, $exception);
}
If you want to customize the validation. Just add this function:
protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator){
$response = new JsonResponse([
'status' => false,
'errors' => $validator->errors()->all(),
], 200);
throw new \Illuminate\Validation\ValidationException($validator, $response);}
And dont forget use Illuminate\Http\JsonResponse;
That's what you told it to do in your exception handler when you overrode the default output to just display $exception->getMessage(). That's the message for a ValidationException.
The built-in exception handler already has handling for both AJAX requests that expect JSON results and ValidationExceptions. If you're going to override it, you'll want to mimic that functionality... but in most cases you should just leave it alone.
The important bit of the default handler is this:
return response()->json([
'message' => $exception->getMessage(),
'errors' => $exception->errors(),
], $exception->status);

How change http resposne status for custom FormRequest?

I use the following class to validate incoming request:
class CreateInvoiceRequest extends FormRequest
{
public function authorize(Request $request)
{
return true;
}
public function messages()
{
return [
'name.required' => 'The name is required.',
'name.string' => 'The name should be a string',
'code.required' => 'Code',
'code.string' => 'Code',
'type.required' => 'Type'
];
}
public function rules()
{
return [
'name' => 'required|string',
'type' => 'string',
'code' => ['required', 'string', new InvoiceCode],
];
}
}
In fail case it returns data in {errors: "The given data was invalid."} object and http status 200.
How and where to change this status? More global question, how to handle errors and warnings in Laravel?
You can add an HTTP status to the ValidationException thrown in the failedValidation method of FormRequest:
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;
class CreateInvoiceRequest extends FormRequest
{
// ...
protected function failedValidation(Validator $validator)
{
throw (new ValidationException($validator))
->status(500)
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
// ...
}

How to test validation of current password in Laravel 5.8

I'm trying to test if the current password is same as in DB.
my simplified controller:
class ChangePasswordController extends Controller
{
public function update(Request $request, User $user)
{
$this->validate($request, [
'current_password' => ['required', new CurrentPassword()],
'password' => 'required|string|min:6|confirmed'
]);
$user->update([
'password' => bcrypt($request->password)
]);
}
}
in my custom CurrentPassword rule i'm checking the hash like this:
class CurrentPassword implements Rule
{
public function passes($attribute, $value)
{
$check = Hash::check($value, auth()->user()->password);
dump($check);
return $check;
}
public function message()
{
return 'Current password is incorrect.';
}
}
and my test for the custom rule is:
/** #test */
public function an_authenticated_user_may_change_own_password()
{
$this->withoutExceptionHandling();
$user = factory(User::class)->create([
'password' => '1234'
]);
$this->actingAs($user)->patch("/profile/{$user->id}/password", [
'current_password' => '1234',
'password' => 'mynewpassword',
'password_confirmation' => 'mynewpassword'
]);
$this->assertTrue(Hash::check('mynewpassword', $user->fresh()->password));
}
unfortunately i'm getting an error:
1)
Tests\Feature\UpdatePasswordTest::an_authenticated_user_may_change_own_password
Illuminate\Validation\ValidationException: The given data was invalid.
i don't understand why this is happining. My dump($check); returns false when i run this test. My $value is '1234' and the auth()->user()->password returns also '1234'. Maybe somebody have an idee what I'm doing wrong.
this test is getting green:
/** #test */
public function current_password_must_be_valid()
{
$user = factory(User::class)->create([
'password' => '1234'
]);
$this->actingAs($user)->patch("/profile/{$user->id}/password", [
'current_password' => '12345',
'password' => 'mynewpassword',
'password_confirmation' => 'mynewpassword'
])->assertSessionHasErrors('current_password');
}
You should hash your password in your factory as well, otherwise Eloquent will store it in cleartext (that's why auth()->user()->password returns '1234')
public function current_password_must_be_valid()
{
$user = factory(User::class)->create([
'password' => Hash::make('1234'); // remember to import the Hash facade
]);
...
}

Laravel Form Request Add Custom Variable After Validation

This is my form request code, i want to add new variable after validation success, so i can access that variable at my controller :
class CouponRequest extends Request
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'start_year' => 'required',
'start_month' => 'required',
'start_day' => 'required',
'start_time' => 'required',
'finish_year' => 'required',
'finish_month' => 'required',
'finish_day' => 'required',
'finish_time' => 'required',
];
}
public function afterValidation()
{
$this->start_date = Carbon::create( $this->start_year, $this->start_month, $this->start_day );
}
}
So after validation has no error, i can call this instance at my controller :
$request->start_date;
Could i do this?
All above methods work but in my opinion I would override the passedValidation method in the form request class. This method is called after the validation checks are passed and hence keep the data clean.
Ex.
public function passedValidation()
{
$this->merge([
'start_date' => Carbon::create( $this->start_year, $this->start_month, $this->start_day )
]);
}
If you dump the data now you should see your new start_date value as well.
You could do this
public function afterValidation()
{
$this->request->add([
'start_date' => Carbon::create($this->start_year, $this->start_month, $this->start_day)
]);
}
public function validate()
{
parent::validate();
$this->afterValidation();
}
And then access the attribute in your controller as
$request->get('start_date');
In your form request use function prepareForValidation()
protected function prepareForValidation(): void
{
$this->merge([
'start_date' => Carbon::now()
]);
}
Cheers!
I am using this method after success request for manipulations.
Source: 50116187/1101038
public function withValidator(Validator $validator)
{
if ( $validator->fails() ) {
\Log::info('Error! No Manipulation!');
}else{
$this->merge([
'custom' => 'Test Manipulation!'
]);
\Log::info('Success Manipulation!');
}
}
I made the validation in the Controller. The method has a "Request $request" parameter. I have a I do this:
$input = $request->all();
$input['my_new_field] = 'the_data';

How to add extra logic on login condition in Laravel 5.2

I just wanted to say if the user is not active, don't allow to login. I have made the controller as below, I am not sure what I am missing or what else I have to do here to make this work!
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Auth\Authenticatable;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class AuthController extends Controller{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
protected $redirectTo = '/home';
public function __construct()
{
$this->middleware($this->guestMiddleware(), ['except' => 'logout']);
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
]);
}
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
}
My thinking was authenticate() method should do the trick!
The below code worked for my case:
protected function getCredentials(Request $request)
{
return [
'email' => $request->input('email'),
'password' => $request->input('password'),
'active' => true
];
}
for Laravel 5.3 need to add following code to LoginController
protected function credentials(Request $request)
{
return [
'email' => $request->input('email'),
'password' => $request->input('password'),
'active' => true
];
}
i think you should create method to check if user passed your credentials, here's my suggestion :
protected function getCredentials(Request $request)
{
return [
'username' => $request->input('email'),
'password' => $request->input('password'),
'active' => true
];
}
and your login method:
public function login(Request $request) {
$this->validate($request,['email' => 'required|email','password' => 'required']);
if (Auth::guard()->attempt($this->getCredentials($request))){
//authentication passed
}
return redirect()->back();
}
hope you get basic idea.
In LoginController.php file write this function
protected function credentials(Request $request) {
$extraFields = [
'user_type'=> 'customer',
'user_entry_status' => 1
];
return array_merge($request->only($this->username(), 'password'), $extraFields);
}
Go to this path :
your-project-folder/vendor/laravel/framework/src/illuminate/Foundation/Auth/AuthenticatesUsers.php
$credentials=$request->only($this->loginUsername(), 'password');
$credentials['status'] = '1';
return $credentials;
Change getCredantials works fine, but it is good practice to let user know, that the account was suspended (credentials are OK, but the account status is not). You can easily override login method in Auth/LoginController.php to your own copy, add your own logic to login process and raise own exception.
in Auth/LoginController.php create login and sendAccountBlocked function
/*load additional classes to LoginController.php*/
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Auth;
public function login(Request $request){
//
$this->validateLogin($request);
//
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
//check user status
if (Auth::user()->user_status == 'A') return $this->sendLoginResponse($request);
// if user_status != 'A' raise exception
else {
$this->guard()->logout();
return $this->sendAccountBlocked($request);
}
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
//
}//
protected function sendAccountBlocked(Request $request){
throw ValidationException::withMessages([
$this->username() => ['Your account was suspended.'],
]);
}

Resources