I'm currently working on a major side project that takes up practically all of my free time. In this project, I had to check an array of submitted files to ensure that the total size was less than 10mb. I couldn't find anything in Laravel's validation docs that could accomplish that for me, so I decided I'd have to write my own custom validation rule.
I was looking for the best possible way to validate array of uploaded files, and I found big part of the solution on StackOverFlow but the problem was that this solution is almost 5 years old. so I decided to spend few minutes writing a quick tutorial on how I achieved my goal and created my own custom validation rule influenced by the solution I found on StackOverFlow.
Let's get started ๐
Table of Content |
---|
1- Input |
2- Routes |
3- Validation |
4- Custom validation |
1- Input:
This is a simple HTML input tag that supports specific picture formats as well as multiple file uploads. The input is wrapped withย a form that POSTย the files to the 'images/upload route.'
<form action="images/upload" method="POST">
<input
type="file"
id="thumbnail"
name="thumbnail"
class="h-full w-full z-50 opacity-0 cursor-pointer"
accept="image/png, image/jpeg, image/jpg, image/gif, image/webp"
multiple
required
/>
</form>
2- Routes:
In routes/web.php
file, we're using the ImageUploadController@store to handle post requests to this route.
Route::post('images/upload', [ImageUploadController::class, 'store'])->name('images.upload');
3- Validation:
I usually don't ever do any validation within my controller, I follow the documentation to create a new request with php artisan make:request StoreImagesRequest
and then Add my validation as showing below.
public function rules()
{
return [
'images' => ['bail', 'required'],
'images.*' => ['bail', 'image', 'mimes:jpeg,jpg,png,gif,webp']
];
}
It really won't matter if you do your validation inside the controller store method, my solution will work either ways.
4- Custom validation:
Following the documentation, next this step should be running the following command php artisan make:rule Maxsize --invokable
and head to app/Rules/Maxsize
file.
A rule object contains a single method: __invoke. This method receives the attribute name, its value, and a callback that should be invoked on failure with the validation error message.
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\InvokableRule;
class Maxsize implements InvokableRule
{
/**
* Run the validation rule.
*
* @param string $attribute
* @param mixed $value
* @param \Closure $fail
* @return void
*/
public function __invoke($attribute, $value, $fail)
{
// Logic goes here
}
}
The above code shows an empty rule that receives Attribute, Value, and Fail. All we need to do is code our validation logic in the invoked function and deliver a response if it fails. If you did not specify an attribute, like in our case, disregard the '$attribute' variable. Your input data will be stored in the '$value' variable, and we can define a response if our validation fails by assigning a string response to the '$fail' variable.
Now, let's get back to our goal. What we want to do here is loop over an array of files, compute the total file size of each file, and then send a failure response if the result is greater than 10.000 kilobytes.
For that, we're going to use array_reduce php function.
array_reduce โ Iteratively reduce the array to a single value using a callback function
public function __invoke($attribute, $value, $fail)
{
$total_size = array_reduce($value, function ($sum, $item) {
// each item is UploadedFile Object
$sum += filesize($item->path());
return $sum;
});
if ($total_size > 10000 * 1000) {
$fail('Total images size should be maximum 10 Megabytes');
}
}
In this code, we're returning a single value out of the array by calculating the total file size. To calculate each file size we're using PHP's function filesize and passing to it the file path using Laravel's helper function path()
And then checking if the total size is over than 10mb then returning the $fail
variable value.
Now we can use our Validation rule by importing it to whatever place we're using for validation, in my case StoreImagesRequest.php
<?php
namespace App\Http\Requests;
use App\Rules\Maxsize;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreImagesRequest 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 rules()
{
return [
'images' => ['bail', 'required', new Maxsize],
'images.*' => ['bail', 'image', 'mimes:jpeg,jpg,png,gif,webp']
];
}
}
I hope this was a clear explanation and please let me know in the comments if you have any questions ๐
Top comments (0)