Laravel 5.2 Custom validation message with custom validation function - validation

I want to create custom validation rule with custom validation error message. For this I created a rule:
$rule => [
'app_id' => 'isValidTag'
]
And for custom message:
$message => [
app_id.isValidTag => 'Not a Valid id'
];
After that I created Service Provider:
class CustomValidationServiceProvider extends ServiceProvider
{
public function boot() {
//parent::boot();
$this->app->validator->resolver(function($transator,$data,$rules,$messages){
return new CustomValidator($transator,$data,$rules,$messages);
});
}
}
And my Custom validation class is:
class CustomValidator extends Validator {
if(empty($parameters)) {
return true;
}
$conext = $parameters[0];
$tag = Tag::where('id', $value)->where('context', $conext)->get();
$flag = false;
if($tag->count() > 0) {
$flag = true;
}
return $flag;
}
All is working fine but the issue is my custom message for app_id.isValidTag is not working even all other message are working fine.
Please suggest me what I missing here or in Laravel 5.2 there is some change to display message. Any idea will be appreciated.

Here is a great tutorial for this: http://itsolutionstuff.com/post/laravel-5-create-custom-validation-rule-exampleexample.html
I think you did it Laravel 4.* way. This is how it is done in Laravel 5.2
in my example where i was making registration authorisation form so files like AuthController.php was premade:
AuthController.php
Validator::make($data, [
...
// add your field for validation
'name_of_the_field' => 'validation_tag', // validation tag from validation.php
...
CustomAuthProvider.php
// if you didn't make a custom provider use Providers/AppServiceProvider.php
public function boot() {
...
Validator::extend('validation_tag', function($attribute, $value, $parameters, $validator) {
// handle here your validation
if ( your_query ) {
return true;
}
return false;
});
validation.php
...
// add your validation tag and message to be displayed
'validation_tag' => 'The field :attribute isn't good',
...
file.blade.php
// to add at the end of the page all your errors add
#if (count($errors) > 0)
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif

Related

Laravel Custom Validation Order

I add a custom Validation Rule
Validator::extend('validate_timezone', function($attribute, $value, $parameters, $validator) {
$items = request('items');
$from_date = Carbon::createFromFormat('Y-m-d H:i:s', $item['from_date']);
// my code below depend on $from_date
......
......
return true;
);
validation rule
"from_date" => "required|date_format:Y-m-d H:i:s|validate_timezone",
The problem, custom validation validate_timezone run before 'date_format:Y-m-d H:i:s', so if the format of date is wrong, I will get error inside validate_timezone function
How can I force to validate date_format:Y-m-d H:i:s before the custom validation validate_timezone?
In the documentation the following can be found:
Rules will be validated in the order they are assigned.
Meaning the code is working as expected.
You're probably looking for the bail option:
Sometimes you may wish to stop running validation rules on an attribute after the first validation failure. To do so, assign the bail rule to the attribute
Which would mean you should try this:
"from_date" => "bail|required|date_format:Y-m-d H:i:s|validate_timezone",
The custom extended validation rule is running first because it's probably defined in the boot function of a service provider which runs on every request, so you need to catch the Carbon exception and return false accordingly
public function boot()
{
\Validator::extend('validate_timezone', function ($attribute, $value, $parameters, $validator) {
try {
$from_date = Carbon::createFromFormat('Y-m-d H:i:s', $value);
return true;
} catch (Exception $ex) {
logger()->warning($ex->getMessage());
return false;
}
});
}
Now if carbon can't create from the format passed, it will thrown an exception that we catch, log and return false
And as #PtrTon mentioned, you need to bail on first validation fail
Now assuming a validation logic like this
Route::post('/', function () {
$validate = request()->validate([
"from_date" => "bail|required|date_format:Y-m-d H:i:s|validate_timezone",
]);
dd($validate);
});
And a view form like this
<form action="/" method="post">
#csrf
<input type="datetime" name="from_date" value="2019-10-16 15:03">
<button type="submit">Submit</button>
</form>
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
The value of the date_from is not valid for both date_format and validate_timezone but only the validation error message of date_format will be returned
Hope this helps

How to Validate File Upload in Laravel [duplicate]

This question already has answers here:
How to Validate on Max File Size in Laravel?
(2 answers)
Closed 4 years ago.
I've completed a tutorial to upload image files. How can I validate file uploads in the view when a user uploads a file larger than 2MB?
create.blade.php
#if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> Errors.<br><br>
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
#if(session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
#endif
<div class="form-group">
<input type="file" name="photos[]" multiple aria-describedby="fileHelp"/>
<small id="fileHelp" class="form-text text-muted">jpeg, png, bmp - 2MB.</small>
</div>
Rules
public function rules()
{
$rules = [
'header' => 'required|max:255',
'description' => 'required',
'date' => 'required',
];
$photos = $this->input('photos');
foreach (range(0, $photos) as $index) {
$rules['photos.' . $index] = 'image|mimes:jpeg,bmp,png|max:2000';
}
return $rules;
}
Everything is ok, however when I try to upload a file which is larger than 2MB it gives me an error:
Illuminate \ Http \ Exceptions \ PostTooLargeException No message
How can I solve this and secure this exception?
in laravel you can not handle this case in controller as it will not get to controller/customrequest and will be handled in middleware so you can handle this in ValidatePostSize.php file:
public function handle($request, Closure $next)
{
// if ($request->server('CONTENT_LENGTH') > $this->getPostMaxSize())
{
// throw new PostTooLargeException;
// }
return $next($request);
}
/**
* Determine the server 'post_max_size' as bytes.
*
* #return int
*/
protected function getPostMaxSize()
{
if (is_numeric($postMaxSize = ini_get('post_max_size'))) {
return (int) $postMaxSize;
}
$metric = strtoupper(substr($postMaxSize, -1));
switch ($metric) {
case 'K':
return (int) $postMaxSize * 1024;
case 'M':
return (int) $postMaxSize * 1048576;
default:
return (int) $postMaxSize;
}
}
with your custom message
Or in App\Exceptions\Handler:
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Http\Exceptions\PostTooLargeException) {
// handle response accordingly
}
return parent::render($request, $exception);
}
Else need to update php.ini
upload_max_filesize = 10MB
If you dont to work with any of above solutions you can use client side validation like if you are using jQuery e.g.:
$(document).on("change", "#elementId", function(e) {
if(this.files[0].size > 7244183) //set required file size 2048 ( 2MB )
{
alert("The file size is too larage");
$('#elemendId').value = "";
}
});
or
<script type="text/javascript">
function ValidateSize(file) {
var FileSize = file.files[0].size / 1024 / 1024; // in MB
if (FileSize > 2) {
alert('File size exceeds 2 MB');
$(file).val(''); //for clearing with Jquery
} else {
}
}
</script>
you have validate image in $rules. try this code:
$this->validate($request,[
'header' => 'required|max:255',
'description' => 'required',
'date' => 'required',
'photos.*' => 'image|mimes:jpeg,bmp,png|max:2000',
]);
Laravel uses its ValidatePostSize middleware to check the post_max_size of the request and then throws the PostTooLargeException if the CONTENT_LENGTH of the request is too big. This means that the exception if thrown way before it even gets to your controller.
What you can do is use the render() method in your App\Exceptions\Handler e.g.
public function render($request, Exception $exception){
if ($exception instanceof PostTooLargeException) {
return response('File too large!', 422);
}
return parent::render($request, $exception);
}
Please note that you have to return a response from this method, you can't just return a string like you can from a controller method.
The above response is to replicate the return 'File too large!'; you have in the example in your question, you can obviously change this to be something else.
Hope this helps!
You can try with putting a custom message inside message() message or add PostTooLargeException handler in Handler class. Something like that:
public function render($request, Exception $exception)
{
...
if($exception instanceof PostTooLargeException){
return redirect()->back()->withErrors("Size of attached file should be less ".ini_get("upload_max_filesize")."B", 'addNote');
}
...
}

Laravel error is not passed to view

I can't figure put why laravel blade doesn't catch my error validation and doesn't pass it to my view.
In detail
I do have error snippet in my blade template
below is my validation which works correctly
What I'm missing?
Thank you
This is json message I see instead of message in blade template
{
message: "The given data was invalid.",
status_code: 500
}
This snippet I use to let user know about error
#if(count($errors))
<div class="form-group">
<div class="alert alert-danger">
<ul>
#if($errors->all())
#foreach($errors->all() as $error)
<li>{{$error}}</li>
#endforeach
#endif
</ul>
</div>
</div> #endif
And finally this is my correctly working validation
$request->validate([
'email' => 'required|email|unique:subscribers|max:255',
]);
EDIT:
This is the rout in web.php
Route::post('saveemail', 'SaveSubscriberEmailController#saveEmail');
And this is the method
namespace App\Http\Controllers;
use App\subscriber;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Ramsey\Uuid\Uuid;
class SaveSubscriberEmailController extends Controller
{
public function saveEmail(Request $request)
{
$request->validate([
'email' => 'required|email|unique:subscribers|max:255',
]);
$uuid = Uuid::uuid4();
$subscriber = new subscriber();
$subscriber->email = $request->email;
$subscriber->uuid = $uuid->toString();
$subscriber->created_at = Carbon::now();
$subscriber->save();
flash('Registration conformation email has been sent. Please check your mailbox. Thank you!')->success();
return redirect()->back();
}
}
I've had this problem before and the way I was able to fix it was to wrap the routes with a middleware group that includes the middleware \Illuminate\View\Middleware\ShareErrorsFromSession::class. It adds the session's errors to the view.
In your Kernel.php class's protected $middlewareGroups array it can look something like:
'web' => [
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
// other middleware
],
Then where you declare your routes you can do:
Route::group(['middleware' => ['web']], function () {
Route::post('saveemail', 'SaveSubscriberEmailController#saveEmail');
};
Request validation only send error of 422 not 500 if you are getting this error it's because of something else and the formRequest error bag won't catch this error .
Route::post('saveemail', 'SaveSubscriberEmailController#saveEmail');
Put this route into web middleware. you can do this like
Route::middleware(['web'])->group(function () {
Route::post('saveemail', 'SaveSubscriberEmailController#saveEmail');
});
Change your controller to this.
class SaveSubscriberEmailController extends Controller
{
public function saveEmail(Request $request)
{
$validator = validate($request->all(),[
'email' => 'required|email|unique:subscribers|max:255',
]);
if($validator->fails()){
return back()->withErrors($validator);
}
$uuid = Uuid::uuid4();
$subscriber = new subscriber();
$subscriber->email = $request->email;
$subscriber->uuid = $uuid->toString();
$subscriber->created_at = Carbon::now();
$subscriber->save();
flash('Registration conformation email has been sent. Please check your mailbox. Thank you!')->success();
return redirect()->back();
}
}
Hope this helps

How do i separate two error message in laravel using one error blade file?

Say,i have two form in one page.I have included one error blade file bellow both of the form. Now when i make wrong in one form & submit it the error message is showing bellow the both form.Its normal.But my question is, how do i separate this two error message,how can i differentiate by giving them two different name?
Give this a try
return redirect()->back()->withErrors([
'form1.name' => 'name is required in Form 1',
'form1.email' => 'email is required in Form 1',
'form2.city' => 'city is required in form 2'
]);
in your view
#if($errors->any())
#foreach ($errors->get('form1.*') as $error) {
{{ $error }}
#endforeach
#endif
So you can group errors by form using array notation form.name and get all with $errors->get('form.*).
Read more about errors here: https://laravel.com/docs/5.4/validation#working-with-error-messages
If you're using Form Request Validation, you can change the errorBag property to get a unique array of errors for your view file.
In your Request file:
class MyFormRequest {
protected $errorBag = 'foobar';
public function rules() { // ... }
}
In your controller:
public function store(MyFormRequest $request) {
// Store entry.
}
Then in your view file:
#if ($errors->foobar->isNotEmpty())
// Work with the errors
#endif
You can use the named error bags.
$validator = Validator::make($request->all(), [
'field1' => 'required',
'field2' => 'required|digits:1',
]);
if ($validator->fails()) {
return back()
->withErrors($validator, 'form1error')
->withInput();
}
To print the error in blade file use-
#if(count($errors->form1error)>0)
<ul>
#foreach($errors->form1error->all() as $error)
<li>{{$error}}</li>
#endforeach
</ul>
#endif

Keeping modal dialog open after validation error laravel

So basically I have a blade.php, controller page and a form request page(validation). I'm trying to keep my modal dialog open if there is an error but I just cant figure it out, what part of code am I missing out on or needs to be changed?
blade.php
<div id="register" class="modal fade" role="dialog">
...
<script type="text/javascript">
if ({{ Input::old('autoOpenModal', 'false') }}) {
//JavaScript code that open up your modal.
$('#register').modal('show');
}
</script>
Controller.php
class ManageAccountsController extends Controller
{
public $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function index()
{
$users = User::orderBy('name')->get();
$roles = Role::all();
return view('manage_accounts', compact('users', 'roles'));
}
public function register(StoreNewUserRequest $request)
{
// process the form here
$this->userRepository->upsert($request);
Session::flash('flash_message', 'User successfully added!');
//$input = Input::except('password', 'password_confirm');
//$input['autoOpenModal'] = 'true'; //Add the auto open indicator flag as an input.
return redirect()->back();
}
}
class UserRepository {
public function upsert($data)
{
// Now we can separate this upsert function here
$user = new User;
$user->name = $data['name'];
$user->email = $data['email'];
$user->password = Hash::make($data['password']);
$user->mobile = $data['mobile'];
$user->role_id = $data['role_id'];
// save our user
$user->save();
return $user;
}
}
request.php
class StoreNewUserRequest extends Request
{
/**
* 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
*/
public function rules()
{
// create the validation rules ------------------------
return [
'name' => 'required', // just a normal required validation
'email' => 'required|email|unique:users', // required and must be unique in the user table
'password' => 'required|min:8|alpha_num',
'password_confirm' => 'required|same:password', // required and has to match the password field
'mobile' => 'required',
'role_id' => 'required'
];
}
}
Laravel automatically checks for errors in the session data and so, an $errors variable is actually always available on all your views. If you want to display a modal when there are any errors present, you can try something like this:
<script type="text/javascript">
#if (count($errors) > 0)
$('#register').modal('show');
#endif
</script>
Put If condition outside from script. This above is not working in my case
#if (count($errors) > 0)
<script type="text/javascript">
$( document ).ready(function() {
$('#exampleModal2').modal('show');
});
</script>
#endif
for possibly multiple modal windows you can expand Thomas Kim's code like following:
<script type="text/javascript">
#if ($errors->has('email_dispatcher')||$errors->has('name_dispatcher')|| ... )
$('#register_dispatcher').modal('show');
#endif
#if ($errors->has('email_driver')||$errors->has('name_driver')|| ... )
$('#register_driver').modal('show');
#endif
...
</script>
where email_dispatcher, name_dispatcher, email_driver, name_driver
are your request names being validated
just replace the name of your modal with "login-modal". To avoid error put it after the jquery file you linked or jquery initialized.
<?php if(count($login_errors)>0) : ?>
<script>
$( document ).ready(function() {
$('#login-modal').modal('show');
});
</script>
<?php endif ?>

Resources