Laravel validation odd behavior - php

I'm working on a file upload and it upload well until I try to get a file I'm not supposed to be able to upload.
My rules are:
public function rules()
{
return [
'media' => ['required', 'image', 'max:2000']
];
}
public function messages()
{
return [
'media.required' => 'You must give a file to upload!',
'media.image' => 'The file is not an image!',
'media.max' => 'The file is too big!',
];
}
and when I try to upload a file which is 2,3Mo I got a 422 but the message is always The given data is invalid without telling me which one is invalid.
Then in my controller, this is how I use it:
public function uploadMedia(AddMediaRequest $request, MyEntity $entity)
{
$filename = $entity->addMedia($request->validated());
return response()->json(['filename' => $filename], 200);
}
Am I missing a simple point ? (I use Vue for the front end with axios)

Related

How to check if the submitted picture is horizontal? Laravel

I created a custom validation rule by using the command:
php artisan make:rule Horizontal
Now, how to check if the submitted picture from my post form is horizontal?
I tried this:
public function passes($attribute, $value)
{
if ($this->request->has('picture')) {
$image = $this->request->get('picture');
if ($image->width < $image->height) {
session()->flash('error', 'Image must be horizontal!');
return false;
}
}
}
public function message()
{
return 'The validation error message.';
}
My Post Request:
public function rules()
{
return [
'title' => 'required|min:2|max:255',
'body' => 'required|min:10',
'picture' => [
'required',
new Horizontal()
]
];
}
After Image upload
Laravel Intervention Image Can help like this:
$img = Image::make('public/foo.jpg');
$img->flip('h');
h for horizontal and v for vertical.
There is one more method orientate to set auto Orientation as well
You can compare height and width as well
This image library required GD library and Imagick plugin
Good luck!

Avoid mimeType validation when no file is uploaded in cakephp 4

Here is the validation for my input file "image" :
public function validationDefault(Validator $validator): Validator
{
$validator = parent::validationDefault($validator);
$validator
->allowEmptyFile('image')
->add('image', 'uploadError', [
'rule' => function ($value, $context) {
foreach ($value as $v) {
return Validation::uploadError($v, true);
}
},
'last' => true,
'message' => 'Upload error'
])
->add('image', 'mimeType', [
'rule' => function ($value, $context) {
foreach ($value as $v) {
return Validation::mimeType($v, [
'image/png',
'image/gif',
'image/pjpeg',
'image/jpeg'
]);
}
},
'message' => 'Bad mime type.',
]);
}
It works well when a file is submitted, but when no file is uploaded the mimeType validation error is triggered.
So I've modified mimeType rule to check if a file is uploaded before checking mimeType like that :
public function validationDefault(Validator $validator): Validator
{
$validator = parent::validationDefault($validator);
$validator
->allowEmptyFile('image')
->add('image', 'uploadError', [
'rule' => function ($value, $context) {
foreach ($value as $v) {
return Validation::uploadError($v, true);
}
},
'last' => true,
'message' => 'Upload error'
])
->add('image', 'mimeType', [
'rule' => function ($value, $context) {
// Added to avoid mimeType validation when no file is uploaded
if ($value[0]->getError() === UPLOAD_ERR_NO_FILE) {
return true;
}
foreach ($value as $v) {
return Validation::mimeType($v, [
'image/png',
'image/gif',
'image/pjpeg',
'image/jpeg'
]);
}
},
'message' => 'Bad mime type.',
]);
}
It works but it doesn't seem so clean to me to add
if ($value[0]->getError() === UPLOAD_ERR_NO_FILE) {return true;}
on every single rule that could be added after mime type check (for example I will add file size check, image width check, etc.)
Is there a better way to add validation rules on file only if a file is submitted ?
Seeing how your upload functionality is currently structured, that is all uploads are stored in the same table, and a behavior handles them via a hasMany association, including for models that only accept a single file, and the frontend using multi-file inputs, one possible solution would be to simply remove the field in case of an empty file upload.
You can change data via the Model.beforeMarshal event/callback, it will run when creating/patching entities, before validation is applied, for example:
public function beforeMarshal(
\Cake\Event\EventInterface $event,
\ArrayAccess $data,
\ArrayObject $options
): void {
if (
isset($data['image'][0]) &&
$data['image'][0] instanceof \Psr\Http\Message\UploadedFileInterface &&
$data['image'][0]->getError() === \UPLOAD_ERR_NO_FILE
) {
unset($data['image']);
}
}
This would remove the image field in case it is an array, and the first element is an empty uploaded file object. This is what you'd receive for a multi-file input where no file has been selected, as well as for your single-file input that uses an array for the name.
Your validation rules would then simply not run when the field isn't present anymore, and when they do run, they would not have to allow that situation, but instead could strictly require valid uploaded file objects.
So, for example for a model that should only accept a single upload, you could do something like this to ensure the value is an array that holds exactly one element with an uploaded file object:
$validator
->add('image', 'exactlyOneUploadedFile', [
'rule' => function ($value, $context) {
if (
is_array($value) &&
count($value) === 1 &&
$value[0] instanceof \Psr\Http\Message\UploadedFileInterface
) {
return true;
}
return false;
},
// ...
])
// ...
Similarly, for a model that should accept multiple uploads, you could do something like this to ensure the value is an array that holds one or more elements of only uploaded file objects:
$validator
->add('image', 'onlyUploadedFiles', [
'rule' => function ($value, $context) {
if (
!is_array($value) ||
count($value) < 1
) {
return false;
}
foreach ($value as $upload) {
if (!($upload instanceof \Psr\Http\Message\UploadedFileInterface)) {
return false;
}
}
return true;
},
// ...
])
// ...
It is important that you really make these checks strict! The validation rules that you've posted would for example only check the first entry in the array, but do not check whether more element exist, which could possibly result in a situation where more than one upload is being sent, and the additional uploads slip through unvalidated!
See also
Cookbook > Database Access & ORM > Saving Data > Modifying Request Data Before Building Entities

For Laravel, How to return a response to client in a inner function

I'm building an api using laravel, the issue is when the client requests my api by calling create() function, and the create()function will call a getValidatedData() function which I want to return validation errors to the client if validation fails or return the validated data to insert database if validation passes, my getValidatedData function is like below so far
protected function getValidatedData(array $data)
{
// don't format this class since the rule:in should avoid space
$validator = Validator::make($data, [
'ID' => 'required',
'weight' => 'required',
]);
if ($validator->fails()) {
exit(Response::make(['message' => 'validation fails', 'errors' => $validator->errors()]));
}
return $data;
}
I don't think exit() is a good way to return the errors message to clients. are there any other ways I can return the laravel Response to clients directly in an inner function. use throwing Exception?
This was what worked for me in Laravel 5.4
protected function innerFunction()
{
$params = [
'error' => 'inner_error_code',
'error_description' => 'inner error full description'
];
response()->json($params, 503)->send();
}
What you can do is using send method, so you can use:
if ($validator->fails()) {
Response::make(['message' => 'validation fails', 'errors' => $validator->errors()])->send();
}
but be aware this is not the best solution, better would be for example throwing exception with those data and adding handling it in Handler class.
EDIT
As sample of usage:
public function index()
{
$this->xxx();
}
protected function xxx()
{
\Response::make(['message' => 'validation fails', 'errors' => ['b']])->send();
dd('xxx');
}
assuming that index method is method in controller you will get as response json and dd('xxx'); won't be executed
You can use this method
public static function Validate($request ,$rolse)
{
// validation data
$validator = Validator::make($request->all(),$rolse);
$errors = $validator->getMessageBag()->toArray();
$first_error = array_key_first($errors);
if (count($errors) > 0)
return 'invalid input params , ' . $errors[$first_error][0];
return false;
}
in controller :
$validate = ValidationHelper::Validate($request,
['title' => 'required']);
if ($validate)
return response()->json(['message' =>'validation fails' , 'error'=> $validate], 403);

How to strict video file to upload using ValidateRequest in laravel

I have a user registration form, where i am using file input box to upload image.
Now the problem is , if i will select video file to upload it will pass ValidateRequest .
In ValidateRequest, i already define the rule for image, below is the code:
class UserValidateRequest extends Request {
public function __construct() {
}
protected $messages = [
'required.password' => 'We need to know your e-mail address!',
];
protected $rules = [
'first_name' => 'required|regex:"[a-zA-Z 0-9]"',
'last_name' => 'regex:"[a-zA-Z 0-9]"',
'image' => ' mimes:jpeg,jpg,png,gif |max:2048',
];
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize() {
return true;
}
public function messages() {
return [
'password.regex' => 'Password shall be 8-20 characters, must have a number and an alphabet', ,
'image.mimes' => 'Upload Gif || JPG || JPEG || PNG Images Only'
];
}
public function attributes() {
return[];
}
}
Instead of using mime type validation, try to add image validation rule to image field:
'image' => 'image|max:2048'

Laravel show errors if request form validation fails

I'm making a REST API that should validate data entry from the user, to achieve that, I made a Request class that has the rules function that it should do the validations.
Request class
class StoreUpdateQuestionRequest extends Request {
public function authorize() {
return true;
}
public function rules() {
$method = $this->method();
$rules = [
'question' => 'required|min:10|max:140',
'active' => 'boolean',
];
return $method !== 'GET' || $method !== 'DELETE' ? $rules : [];
}
}
So, in the controller, when I try to run an endpoint which it fires the validations, it does work, it fails when it's has to, but it doesn't show me the errors as I expect, even though I defined the error messages in the messages function contained in the Request class, instead of showing me that errors, it redirects me to a location. Which sometimes is the result of a request I made before or it runs the / route, weird.
Controller function
public function store(StoreUpdateQuestionRequest $request) {
$question = new Question;
$question->question = $request->question;
$question->active = $request->active;
if($question->save()) {
$result = [
'message' => 'A question has been added!',
'question' => $question,
];
return response()->json($result, 201);
}
}
Any ideas? Thanks in advance!
In order to make this work, you have to add an extra header to your request:
Accept: application/json
That did the trick.
You can use controller based validation as described in documentation https://laravel.com/docs/5.2/validation#manually-creating-validators
public function store(Request $request) {
$validator = Validator::make($request->all(), [
'question' => 'required|min:10|max:140',
'active' => 'boolean',
]);
if ($validator->fails()) {
return response()->json($validator->errors());
}
//other
}

Categories