Laravel: custom multidimensional array validation - php

I've a form which user will insert many records at a time. Each record will have an id, a start date and an end date. To process the input data, I'm looking for the best way to validate all these things.
I'll have to require at least one record
For each inputed record, id should exists at another table, date start and end date should be valid dates and end date should be older than start date
So I need some sort of multidimensional array validation here... Is there any custom validation plugin/code already coded for that?
I've tried to extend Laravel validation but I couldn't get even close to what I'd like...
What I've tried:
app/services/validators/LearningPathValidator.php (I'm using laravel-extended-validator)
<?php
use Crhayes\Validation\ContextualValidator;
class LearningPathValidator extends ContextualValidator
{
protected $rules = [
'default' => [
'name' => 'required|max:96',
'courses' => 'required|multi_array:course_id=required;exists:courses,date_start=required;date_format:d/m/Y,date_end=required;date_format:d/m/Y'
],
];
}
app/validations.php (Here I'm extending Illuminate\Validation\Validator class)
<?php
class AppValidator extends Illuminate\Validation\Validator
{
protected function validateMultiArray($attribute, $value, $parameters)
{
if (!is_array($value)) {
return false;
}
foreach ($parameters as $parameter) {
list($_attribute, $rules) = $this->parseRule(
str_replace(['=', ';'], [':', ','], $parameter));
foreach ($rules as $rule) {
foreach (array_keys(Input::get($attribute)) as $idx){
$this->validate(sprintf('%s.%d.%s', $attribute, $idx,
snake_case($_attribute)), $rule);
}
}
}
return count($this->messages->all()) === 0;
}
}
My start/global.php: (Here I extend Illuminate\Validation\Validator with AppValidator)
// ...
Validator::resolver(function($translator, $data, $rules, $messages) {
return new AppValidator($translator, $data, $rules, $messages);
});
// ...
My models are using courses[$index][course_id], courses[$index][date_start] and courses[$index][date_end] as field names.
Actually I can't require at least one record as I said before and I can't assure end date will be older than start date. Any suggestions to rewrite what I've coded? Thank you in advance!

I created a package to do just this as I encountered the same problem with data from AngularJS.
https://github.com/lakedawson/vocal

Related

How to get all possible error messages of a Laravel Controller method

So I have a Laravel Application, which has many Controllers to handle various aspects of the applications.
Now each controller has various methods. Most of the methods have validations rules defined such as:
$validationArray = [
'id'=>'required|integer',
'status'=>'required|string'
];
$validator = Validator::make($request->all(),$validationArray);
if ($validator->fails()){
return Response::json(['response'=>implode(', ',$validator->messages()->all())],422);
}
Now the following line:
return Response::json(['response'=>implode(', ',$validator->messages()->all())],422);
actually returns whatever is wrong with the validation rules.
My question is: Is there any way to get all possible error messages programmatically?
Of course, one way to do it is going around the rule by rule and make a list manually but there are hundreds of the methods scattered over various controllers.
So, if anyone could point me in the direction of taking all the error messages in some easier way, would be much appreciated.
Thank you in advance!
UPDATE
So to clear further I need a list of all possible errors, like for above code the list will be like:
['id is required', 'id must be an integer', 'status is required', 'status must be an string']
UPDATE 2
Please keep in mind that there are hundreds of methods and also I do not want to change the final response of the method but to have some sort of external script which can help me getting the error messages without interfering with the controllers much.
In order to do that you have to extend Validator class and write a method that will iterate all rules and explicitly add error messages as if they failed.
First, create a new file app\Http\Custom\Validator.php:
<?php
namespace App\Http\Custom;
use Illuminate\Contracts\Validation\Rule as RuleContract;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\ValidationRuleParser;
use Illuminate\Validation\Validator as BaseValidator;
class Validator extends BaseValidator {
/** #var MessageBag */
protected $errorMessages;
/** #var array */
protected $hasExplicitFileErrorMessage;
protected $explicitFileRules = [
'File', 'Image', 'Mimes', 'Mimetypes', 'Dimensions',
];
function availableErrors()
{
$this->errorMessages = new MessageBag();
$this->hasExplicitFileErrorMessage = [];
foreach($this->rules as $attribute => $rules) {
$attribute = str_replace('\.', '->', $attribute);
foreach($rules as $rule) {
[$rule, $parameters] = ValidationRuleParser::parse($rule);
if($rule == '') {
continue;
}
if(($keys = $this->getExplicitKeys($attribute)) &&
$this->dependsOnOtherFields($rule)) {
$parameters = $this->replaceAsterisksInParameters($parameters, $keys);
}
// explicitly add "failed to upload" error
if($this->hasRule($attribute, $this->explicitFileRules) && !in_array($attribute, $this->hasExplicitFileErrorMessage)) {
$this->addFailureMessage($attribute, 'uploaded', []);
$this->hasExplicitFileErrorMessage[] = $attribute;
}
if($rule instanceof RuleContract) {
$messages = $rule->message() ? (array)$rule->message() : [get_class($rule)];
foreach($messages as $message) {
$this->addFailureMessage($attribute, get_class($rule), [], $message);
}
} else {
$this->addFailureMessage($attribute, $rule, $parameters);
}
}
}
return $this->errorMessages->all();
}
function addFailureMessage($attribute, $rule, $parameters = [], $rawMessage = null)
{
$this->errorMessages->add($attribute, $this->makeReplacements(
$rawMessage ?? $this->getMessage($attribute, $rule), $attribute, $rule, $parameters
));
}
// we have to override this method since file-type errors depends on data value rather than rule type
protected function getAttributeType($attribute)
{
if($this->hasRule($attribute, $this->explicitFileRules)) {
return 'file';
}
return parent::getAttributeType($attribute);
}
}
Next, let's register this class in Validation factory:
<?php
namespace App\Providers;
use App\Http\Custom\Validator; // <-- our custom validator
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
public function boot()
{
app('validator')->resolver(function ($translator, $data, $rules, $messages) {
return new Validator($translator, $data, $rules, $messages);
});
}
}
And... that's all. Let's test it:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class HomeController extends Controller {
function index(Request $request)
{
$rules = [
'id' => 'required|int|between:2,10',
'status' => 'required_with:nonexisting|string|email',
'avatar' => 'required|file|mimes:png|max:1000',
'company' => 'required_without:id|unique:companies,id'
];
$validator = Validator::make([], $rules);
dump($validator->availableErrors());
}
}
array:13 [▼
0 => "The id field is required."
1 => "The id must be an integer."
2 => "The id must be between 2 and 10."
3 => "The status field is required when nonexisting is present."
4 => "The status must be a string."
5 => "The status must be a valid email address."
6 => "The avatar failed to upload."
7 => "The avatar field is required."
8 => "The avatar must be a file."
9 => "The avatar must be a file of type: png."
10 => "The avatar may not be greater than 1000 kilobytes."
11 => "The company field is required when id is not present."
12 => "The company has already been taken."
]
It isn't pretty but here's my shot:
$validationArray = [
'id'=>'required|integer',
'status'=>'required|string'
];
$validator = Validator::make($request->all(), $validationArray);
if ($validator->fails()) {
$messages = [];
$invalid_fields = array_keys($validator->messages()->toArray());
$rules = $v->getRules();
foreach($invalid_fields as $invalid_field) {
foreach($rules[$invalid_field] as $rule) {
if(str_contains($rule, ':') {
// complex rules that have parameters (min, between, size, format)
// are more difficult to work with. I haven't figured out how to do them yet
// but you should get the idea.
continue;
} else {
$messages[] = str_replace(':attribute', $invalid_field, $validator->getTranslator()->get("validation.$rule"));
}
}
}
return Response::json(['response' => implode(', ', $messages)], 422);
}
Number 1: Like I mentioned in my comment under the question, what you're trying to achieve may be done in simpler way.
Number 2: Since you do not want to change your already written code where you got ->messages() then you could do the following. I will list the steps and provide an example code.
We need to override Laravel's validator, (Validation) Factory, and ValidationService provider classes.
In App\Services folder you can create two classes Validator and ValidationFactory
in App\Providers create a class ValidationServiceProvider
Go into config/app.php file and under providers replace Illuminate\Validation\ValidationServiceProvider::class with App\Providers\ValidationServiceProvider::class
Validator class looks like so:
namespace App\Services;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\ValidationRuleParser;
use Illuminate\Contracts\Translation\Translator;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Illuminate\Contracts\Validation\Rule as RuleContract;
class Validator extends \Illuminate\Validation\Validator
{
/**
* #var MessageBag $all_messages
*/
protected $all_messages;
public function __construct(Translator $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
{
parent::__construct($translator, $data, $rules, $messages, $customAttributes);
$this->all_messages = new MessageBag;
$this->getAllFormattedMessages();
}
public function makeAllRulesMessages($attribute, $rule, $parameters)
{
$this->all_messages->add($attribute, $this->makeReplacements(
$this->getMessage($attribute, $rule), $attribute, $rule, $parameters
));
}
public function messages(bool $validated_rules_messages = false)
{
return $validated_rules_messages
? $this->validatedMessages()
: $this->all_messages;
}
/**
* This is here in case the true validated messages are needed
*
* #return MessageBag
*/
public function validatedMessages()
{
return parent::messages();
}
public function getAllFormattedMessages()
{
// We'll spin through each rule and add all messages to it.
foreach ($this->rules as $attribute => $rules) {
$attribute = str_replace('\.', '->', $attribute);
foreach ($rules as $rule) {
// First we will get the correct keys for the given attribute in case the field is nested in
// an array. Then we determine if the given rule accepts other field names as parameters.
// If so, we will replace any asterisks found in the parameters with the correct keys.
[$rule, $parameters] = ValidationRuleParser::parse($rule);
if (($keys = $this->getExplicitKeys($attribute)) &&
$this->dependsOnOtherFields($rule)) {
$parameters = $this->replaceAsterisksInParameters($parameters, $keys);
}
$value = $this->getValue($attribute);
if ($value instanceof UploadedFile && $this->hasRule($attribute, array_merge($this->fileRules, $this->implicitRules))
) {
$this->makeAllRulesMessages($attribute, 'uploaded', []);
} elseif ($rule instanceof RuleContract) {
$this->makeCustomRuleMessage($attribute, $rule);
} else {
$this->makeAllRulesMessages($attribute, $rule, $parameters);
}
}
}
}
/**
* #param $attribute
* #param \Illuminate\Contracts\Validation\Rule $rule $rule
*/
public function makeCustomRuleMessage($attribute, $rule)
{
$this->failedRules[$attribute][get_class($rule)] = [];
$messages = (array)$rule->message();
foreach ($messages as $message) {
$this->all_messages->add($attribute, $this->makeReplacements(
$message, $attribute, get_class($rule), []
));
}
}
}
This class does one thing in summary, get all the messages of the passed rules into $all_messages property of the class. It extends and allows the base validation class run, and simply overrides messages() method to make all the collected rules available for use.
ValidationFactory overrides Illuminate\Validation\Factory and it looks like so:
namespace App\Services;
use Illuminate\Validation\Factory;
class ValidationFactory extends Factory
{
/**
* Resolve a new Validator instance.
*
* #param array $data
* #param array $rules
* #param array $messages
* #param array $customAttributes
* #return \Illuminate\Validation\Validator
*/
protected function resolve(array $data, array $rules, array $messages, array $customAttributes)
{
if (is_null($this->resolver)) {
return new \App\Services\Validator($this->translator, $data, $rules, $messages, $customAttributes);
}
return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
}
}
This class does only one thing, overrides resolve() method in this class by making use of the instance of our custom \App\Services\Validator class instead.
ValidationServiceProvider extends Illuminate\Validation\ValidationServiceProvider and overrides registerValidationFactory() method and it looks like so:
namespace App\Providers;
use App\Services\ValidationFactory;
use Illuminate\Validation\ValidationServiceProvider as BaseValidationServiceProvider;
class ValidationServiceProvider extends BaseValidationServiceProvider
{
protected function registerValidationFactory()
{
$this->app->singleton('validator', function ($app) {
$validator = new ValidationFactory($app['translator'], $app);
// The validation presence verifier is responsible for determining the existence of
// values in a given data collection which is typically a relational database or
// other persistent data stores. It is used to check for "uniqueness" as well.
if (isset($app['db'], $app['validation.presence'])) {
$validator->setPresenceVerifier($app['validation.presence']);
}
return $validator;
});
}
}
What the above class does is also to instruct the provide to make use of our App\Services\ValidationFactory whenever the app requires one.
And we are done. All validation messages will be shown even if one of our validation rules failed.
Caveats
In order to achieve this, we needed to make a lot of changes and overriding. Except really critical this may signal that something about the app's design looks wrong.
Laravel validation implementation may change in future release and therefore may become a problem maintaining these changes.
I cannot tell if there are other side effects that might happen for overriding Laravel's default validation implementation or if all the rules return the right messages.
Normally you only want to return failed validation messages to user rather than all the possible failures.
I think that functions failed() (get the failed validation rules) or errors() (get the message container for the validator) may help you. If it does not - go to https://laravel.com/api/5.8/Illuminate/Validation/Validator.html and I hope that you find needed function.
I think you are looking for a way to have custom error messages. if this is the case then the answer is like this:
$messages = [
'id.required' => 'id is required',
'id.integer' => 'id must be an integer',
'status.required' => 'status is required',
'status.string'=> 'status must be an string'
];
$validationArray = [
'id'=>'required|integer',
'status'=>'required|string'
];
$validator = Validator::make($request->all(),$validationArray, $messages);
more info you can find here.
I hope this is what you are looking for and my answer is helping you:)
Based on the Laravel Form Validation Procedure you can write the statement by following:
$validationArray = [
'id'=>'required|integer',
'status'=>'required|string'
];
$validator = Validator::make($request->all(),$validationArray);
if ($validator->fails()){
return Response::json(['response'=> validator->errors())],422);
}
Where errors() method return all the errors as associative array where the message will be associate with the field name accordingly and that's how you can get the errors.

Create rule to make request only contain certain keys

I am using the Lumen Framework, which utilizes the Laravel Validation
I wanted to create a Validator Rule to make the Request->input() json only contain specific keys at the root like "domain" and "nameservers". Not more and not less.
Example passing the rule:
{
"domain":"domain.tld",
"nameservers":
{...}
}
Example not passing the rule:
{
"domain":"domain.tld",
"nameservers":
{...},
"Hack":"executeSomething()"
}
I tried to use to use several default validation rules to achieve this but wasnt successful.
My approach was now to put the request in another array like this
$checkInput['input'] = $request->all();
to make the validator validate the "root" keys.
Now this is my Approach:
create the validator
$checkInput['input'] = $request->all();
$validator = Validator::make($checkInput, [
'input' => [
'onlyContains:domain,nameservers'
],
]);
creating the rule
Validator::extend('onlyContains', function($attribute, $value, $parameters, $validator){
$input = $validator->getData();
$ok = 0;
foreach ($parameters as $key => $value) {
if (Arr::has($input, $attribute . '.' . $value)) {
$ok++;
}
}
if (sizeof(Arr::get($input, $attribute)) - $ok > 0) {
return false;
}
return true;
});
It seems i got the desired result, but i am asking if there is maybe smarter solution to this with the default rules provided by Laravel/Lumen.
You are trying to do a blacklisting approach blocking out fields that are not intended. A simple approach, that is utilized a lot, is to only fetch out the validated. Also you are trying to do logic, that goes against normal validation logic, to do it a field at a time.
This is also a good time, to learn about FormRequest and how you can get that logic, into a place where it makes more sense.
public function route(MyRequest $request) {
$input = $request->validated();
}
With this approach, you will only ever have the validated fields in the $input variable. As an extra bonus, this approach will make your code way easier to pick up by other Laravel developers. Example form request below.
public class MyRequest extends FormRequest
{
public function rules()
{
return [
'domain' => ['required', 'string'],
'nameservers' => ['required', 'array'],
];
}
}
You should use prohibited rule.
For eg:
$allowedKeys = ['domain', 'nameservers'];
$inputData = $request->all();
$inputKeys = array_keys($inputData);
$diffKeys = array_diff($inputKeys, $allowedKeys);
$rules = [];
foreach($diffKeys as $value) {
$rules[$value] = ['prohibited'];
}

How to validate date time with many format in laravel

I want to check my request datetime with many format look like below format:
Y-m-d
Y-m-
Y-m
In Laravel, I use Validator to validate datetime, but Validator can not make checking with many format. This is my code:
Validator::make(['date' => $departureDate], [
'date' => 'required|date_format:Y-m-d, Y-m, Y-m-'
]);
How can I do it in laravel
Please help me! Many thanks!
You must write a custom validation format for that. Laravel's date_format expects only one parameter and not capable of handling multi-formats. There are Two ways to add custom validation. first, one is making a rule repository and add your validation logic there. Here Taylor Otwell explained this method.
The other way to doing that is extend validation in app service provider and add new rule there. add this code in app service provider:
use Illuminate\Support\Facades\Validator;
Validator::extend('multi_date_format', function ($attribute, $value, $parameters,$validator) {
$ok = true;
$result = [];
// iterate through all formats
foreach ($parameters as $parameter){
//validate with laravels standard date format validation
$result[] = $validator->validateDateFormat($attribute,$value,[$parameter]);
}
//if none of result array is true. it sets ok to false
if(!in_array(true,$result)){
$ok = false;
$validator->setCustomMessages(['multi_date_format' => 'The format must be one of Y-m-d ,Y-m or Y-m-']);
}
return $ok;
});
And here you can use it this way:
$validator = Validator::make(['date' => '2000-02-01'], [
'date' => 'required|multi_date_format:Y-m-d,Y-m,Y-m-'
]);
if($validator->fails()) {
$errors = $validator->errors()->all();
}
You can register custom validator in file app/Providers/AppServiceProvider.php in boot method.
Validator::extend('several_date_format', function ($attribute, $value, $parameters,$validator) {
foreach ($parameters as $parameter){
if (!$validator->validateDateFormat($attribute,$value,[$parameter]))
return false;
}
return true;
});
Now you can use it
'your_date' => 'required|several_date_format:Y-m-d,...'
Optional after it you can add custom message in resources/lang/en/validation.php
return [
...
'several_date_format' => 'Error text'
...
]

Laravel validation - comma separated string input as array

How can I validate one input with multiple values? I'm using bootstrap tagsinput plugin. It returns all tags in one field. I need to validate this tags - unique.
First I'm trying to place this tags into array and then validate it in request but still no luck.
Here is my code in request:
public function all()
{
$postData = parent::all();
// checkbox status
if(array_key_exists('keywords', $postData)) {
// put keywords into array
$keywords = explode(',', $postData['keywords']);
$test = [];
$i = 0;
foreach($keywords as $keyword)
{
$test[$i] = $keyword;
$i++;
}
$postData['keywords'] = $test;
}
return $postData;
}
public function rules()
{
$rules = [
'title' => 'required|min:3|unique:subdomain_categories,title|unique:subdomain_keywords,keyword',
'description' => '',
'image' => 'required|image',
'keywords.*' => 'min:3'
];
return $rules;
}
But as soon as keyword becomes invalid I get this error:
ErrorException in helpers.php line 531:
htmlentities() expects parameter 1 to be string, array given.
Any ideas what's wrong?
I was running into a similar problem with comma separated emails on 5.4. Here's how I solved it:
In your request class, override the prepareForValidation() method (which does nothing by default, btw), and explode your comma separated string.
/**
* #inheritDoc
*/
protected function prepareForValidation()
{
$this->replace(['keywords' => explode(',', $this->keywords)]);
}
Now you can just use Laravel's normal array validation!
Since my case needed to be a bit more explicit on the messages, too, I added in some attribute and message customizations as well. I made a gist, if that's of interest to you as well
Instead of mutating your input, for Laravel 6+ there is a package to apply these validations to a comma separated string of values:
https://github.com/spatie/laravel-validation-rules#delimited
You need to install it:
composer require spatie/laravel-validation-rules
Then, you can use it as a rule (using a FormRequest is recommended).
For example, to validate all items are emails, not long of 20 characters and there are at least 3 of them, you can use:
use Spatie\ValidationRules\Rules\Delimited;
// ...
public function rules()
{
return [
'emails' => [(new Delimited('email|max:20'))->min(3)],
];
}
The constructor of the validator accepts a validation rule string, a validation instance, or an array. That rules are used to validate all separate values; in this case, 'email|max:20'.
It's not a good practice to override the all() method to validate a field.
If you receive a comma separated String and not an array that you want to validate, but there is no common Method available, write your own.
in your App/Providers/ValidatorServiceProvider just add a new Validation:
public function boot()
{
Validator::extend('keywords', function ($attribute, $value, $parameters, $validator)
{
// put keywords into array
$keywords = explode(',', $value);
foreach($keywords as $keyword)
{
// do validation logic
if(strlen($keyword) < 3)
{
return false;
}
}
return true;
}
}
Now you can use this Validation Rule on any request if needed, it is reusable.
public function rules()
{
$rules = [
'title' => 'required|min:3|unique:subdomain_categories,title|unique:subdomain_keywords,keyword',
'description' => 'nullable',
'image' => 'required|image',
'keywords' => 'keywords',
];
return $rules;
}
If the validation pass and you want to make the keywords be accessible in your request as array, write your own method:
public function keywords ()
{
return explode(',', $this->get('keywords'));
}

Laravel validation on checkbox which named as array

I've some areas in my form something like:
<ul>
<li>
<input type="checkbox" name="post_categories[]" value="16">English First Main Category<br>
<ul>
<li><input type="checkbox" name="post_categories[]" value="17">English First Subcategory<br></li>
</ul>
</li>
</ul>
When I try to validate them as required fields or something else, Laravel did not validate rules. My rules something like below (In /application/models/posts.php):
public static $rules = array(
'post_title' => 'required',
'post_body' => 'required',
'content_language'=>'required|alpha',
'post_categories'=>'array_numeric',
'post_sequence_number'=>'numeric'
);
public static function validate($data){
return Validator::make($data, static::$rules);
}
In /application/library/validate.php I've a function that validates the array is numeric or not:
Class Validator extends Laravel\Validator {
public function validate_array_numeric($attribute, $value, $parameters){
$numeric_values = array_filter($value, create_function('$item', 'return (is_numeric($item));'));
return count($numeric_values) == count($value);
}
}
Rules works perfectly, except post_categories[]. I get the error:
Method [array_numeric] does not exist.
Cheers.
I had to solve the same problem. Here's what I did:
I created a custom class that extends the default Laravel\Validator class. I wanted to be able to tell the validator when I'm dealing with multiple values (like in your case). In my implementation this could be done by appending '_array' to every validation rule for a certain field name. What my class does is to check if the rule name has this suffix and if it does the value parameter (which is an array in this case) is broken down to its contained items and passed to the default validation functions in Laravel.
<?php
class Validator extends Laravel\Validator {
public function __call($method, $parameters)
{
if (substr($method, -6) === '_array')
{
$method = substr($method, 0, -6);
$values = $parameters[1];
$success = true;
foreach ($values as $value) {
$parameters[1] = $value;
$success &= call_user_func_array(array($this, $method), $parameters);
}
return $success;
}
else
{
return parent::__call($method, $parameters);
}
}
protected function getMessage($attribute, $rule)
{
if (substr($rule, -6) === '_array')
{
$rule = substr($rule, 0, -6);
}
return parent::getMessage($attribute, $rule);
}
}
As stated in the posts above you will have to make the following change so that your custom Validator class can be found by the Autoloader:
Then you need to remove the following line from
application/config/application.php file:
'Validator' => 'Laravel\Validator'
When this is done you'll be able to use all of Laravel's validation rules with the '_array' suffix. Here's an example:
public static $rules = array(
'post_categories'=>'required_array|alpha_array'
);
I don't know if this issue has been solved in Laravel 4. Maybe you can try it.
But what I'm doing right now is extending the validation class.
You can create a new library that extends the validation class.
To validate if all the items in the array has numeric values. This is in application/libraries:
class Validator extends Laravel\Validator {
public function validate_arraynumeric($attribute, $value, $parameters){
$numeric_values = array_filter($value, create_function('$item', 'return (is_numeric($item));'));
return count($numeric_values) == count($value);
}
}
To change the default error message when the validation fails. Go to application/language/en/validation.php. Just use the name of the function as the key for the new array item:
"arraynumeric" => "The :attribute contains non-numeric values",
update
Then you need to remove the following line from application/config/application.php file:
'Validator' => 'Laravel\\Validator'
To use the new validation:
public static $rules = array(
'post_categories'=>'array_numeric'
);
Now for the required checkbox. I assume you're just requiring one checkbox to be checked. You can just check in the function for the new validation the count of the checked checkboxes if it is at least 1.
You're doing something strange here.
Using post_categories[] as form names generates an array. This means you cannot validate it with 'post_categories[]'=>'required|numeric'. Instead you have to loop through the array and validate each field on it's own.

Categories