To validate a single file (a single input-file-field) is no problem in Yii and it works very well:
public function rules() {
return CMap::mergeArray(parent::rules(), array(
array(
'image',
'file',
'allowEmpty'=>false,
'types'=>'pdf,jpg,gif,png',
'on' => 'CaseA, CaseB',
'maxSize'=>1024000,
'tooLarge' => 'Max File size is 1MB',
'message' => 'You have to upload a file at least'
),
....
....
What if image is an array of files? Created Form-Elements by Yii looks like this (Firebug-Output):
<input type="file" id="MyFormModel[image][0]" name="MyFormModel[image][0]">
<input type="file" id="MyFormModel[image][1]" name="MyFormModel[image][1]">
What exactly should I modify in my rules? A simple "image[]" does not help.
EDIT: Solution
public function rules() {
return CMap::mergeArray(parent::rules(), array(
array(
'image',
'file',
'allowEmpty'=>false,
'types'=>'pdf,jpg,gif,png',
'on' => 'CaseA, CaseB',
'maxSize'=>1024000,
'maxFiles' => 5, // <----- THAT'S IT
'tooLarge' => 'Max File size is 1MB',
'message' => 'You have to upload a file at least'
),
....
....
Set the maxFiles attribute appropriately and the validator will work for all of the inputs. You don't need to change the input control names.
Related
I am trying to upload image by cakephp 3. I have tried the code below which was working in cakephp 2. Now in cakephp 3 I have tried the code below
database field
ALTER TABLE `users` ADD `avatar` VARCHAR(255) NOT NULL ;
Then in users add.ctp I have created below form
<?= $this->Form->create($user,['type' => 'file']) ?>
<?= $this->Form->input('avatar',['type' => 'file']);?>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
In user model I have added this validation
$validator
->add('avatar', [
'uploadError' => [
'rule' => 'uploadError',
'message' => 'The cover image upload failed.',
'allowEmpty' => TRUE,
],
'mimeType' => [
'rule' => array('mimeType', array('image/gif', 'image/png', 'image/jpg', 'image/jpeg')),
'message' => 'Please only upload images (gif, png, jpg).',
'allowEmpty' => TRUE,
],
'fileSize' => [
'rule' => array('fileSize', '<=', '1MB'),
'message' => 'Cover image must be less than 1MB.',
'allowEmpty' => TRUE,
],
'processCoverUpload' => [
'rule' => 'processCoverUpload',
'message' => 'Unable to process cover image upload.',
'allowEmpty' => TRUE,
],
]);
After that I found this error
ext/fileinfo is required for validating file mime types
After ndm's comment, I have changed
;extension=php_fileinfo.dll
to
extension=php_fileinfo.dll
in the php.ini file
Then this error has been gone. But the new error that I found
error : Method processCoverUpload does not exist
But in the method below I have added
public function processCoverUpload($check = array()) {
if (!is_uploaded_file($check['avatar']['tmp_name'])) {
return FALSE;
}
if (!move_uploaded_file($check['avatar']['tmp_name'], WWW_ROOT . 'img' . DS . 'uploads' . DS . $check['avatar']['name'])) {
return FALSE;
}
$this->data[$this->alias]['avatar'] = 'uploads/'. $check['avatar']['name'];
return TRUE;
}
I don't know why Method processCoverUpload does not exist. Can you explain?
As already mentioned in the comments, CakePHP 3.x now requires the fileinfo extension for validating mime types.
http://php.net/manual/en/fileinfo.installation.php
The other error happens because you haven't defined a provider where your custom method can be found. You've most likely added the method to your table class, so in that case you should use the table provider
'processCoverUpload' => [
'provider' => 'table', // <<<< there you go
'rule' => 'processCoverUpload',
'message' => 'Unable to process cover image upload.',
'allowEmpty' => TRUE,
],
The default provider is defined to use the \Cake\Validation\Validation class.
On a side note, $check wont contain the field name as a key, validation methods will receive the plain value that the key in the data holds.
Also $this->data doesn't exist anymore, things are working different now in 3.x. If you want to modify the data, then you should use the beforeMarshal or beforeSave events. However, with the latter you'll run into problems in case the upload field is using the same name as the column in the database which stores the file path, as this will cause the array to be marshalled to the type of the column, which is most probably a string type: https://github.com/cakephp/cakephp/issues/5998
See also
Cookbook > Database Access & ORM > Validation > Validation Providers
Cookbook > Validation > Adding Validation Providers
Cookbook > ... ORM > Saving Data > Modifying Request Data Before Building Entities
I try to use fileimagearbehavior extension for Yii, and its not a lot documentation for that. In examples I just found how can 1 file be uploaded, but I cant find way how to upload more than 1 file. Here example
class Dish extends CActiveRecord {
public $recipeImg; // used by the form to send the file.
public function rules()
{
return array(
// ...
// for the form too
array('recipeImg', 'file', 'types' => 'png, gif, jpg', 'allowEmpty' => true),
// ...
);
}
public function behaviors() {
return array(
'recipeImgBehavior' => array(
'class' => 'ImageARBehavior',
'attribute' => 'recipeImg', // this must exist
'extension' => 'png, gif, jpg', // possible extensions, comma separated
'prefix' => 'img_',
'relativeWebRootFolder' => 'files/recipes', // this folder must exist
# 'forceExt' => png, // this is the default, every saved image will be a png one.
# Set to null if you want to keep the original format
'useImageMagick' => '/usr/bin', # I want to use imagemagick instead of GD, and
# it is located in /usr/bin on my computer.
// this will define formats for the image.
// The format 'normal' always exist. This is the default format, by default no
// suffix or no processing is enabled.
'formats' => array(
// create a thumbnail grayscale format
'thumb' => array(
'suffix' => '_thumb',
'process' => array('resize' => array(60, 60), 'grayscale' => true),
),
// create a large one (in fact, no resize is applied)
'large' => array(
'suffix' => '_large',
),
// and override the default :
'normal' => array(
'process' => array('resize' => array(200, 200)),
),
),
'defaultName' => 'default', // when no file is associated, this one is used by getFileUrl
// defaultName need to exist in the relativeWebRootFolder path, and prefixed by prefix,
// and with one of the possible extensions. if multiple formats are used, a default file must exist
// for each format. Name is constructed like this :
// {prefix}{name of the default file}{suffix}{one of the extension}
)
);
}
}
And here piece of view:
echo $form->fileField($model,'recipe');
I have a form where users can upload images, and I'm printing it to the page like so:
<?php echo $this->Form->label('file', 'Image file', array('class' => 'col-lg-1 control-label')); ?>
Then, in the model I'm setting up validation like so:
public $validate = array(
'file' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'You must select an image to upload'
),
'extension' => array(
'rule' => array('extension', array('png')),
'message' => 'Images must be in PNG format'
),
'size' => array(
'rule' => array('fileSize', '<', '1MB'),
'message' => 'Images must be no larger than 1MB'
),
'goodUpload' => array(
'rule' => 'uploadError',
'message' => 'Something went wrong with the upload, please try again'
)
)
);
However, Cake doesn't seem to be associating the form field with the validation rule, as if I select an image to upload I always get "You must select an image to upload" as a form error. I've made sure the form has enctype="multipart/form-data".
Is this happening because file isn't a database field? How can I make cake run some validation on file?
Edit: Here's my entire form, as requested: http://pastebin.com/SbSbtDP9
You can validate fields that are not in the database, long as you have the correct field name in the correct model.
From what I can see in your code it seems your outputting a label instead of an actual input, for the image upload I would try
echo $this->Form->create('Model', array('type'=>'file'));
echo $this->Form->input('file', array('type'=>'file'));
echo $this->Form->submit('Upload Image'):
echo $this->Form->end();
For the validation I would try something like with the rest of your validate options (size, etc...) CakePHP usually throws an error on notEmpty on File Uploads. So just checking for the extension type is usually good enough.
public $validate = array(
'file' => array(
'rule' => array(
'extension', array('jpeg', 'jpg')
'message' => 'You must supply a file.'
)
)
);
Majority of time in CakePHP for Image Uploading I resort to a plugin such as https://github.com/josegonzalez/cakephp-upload it does validation and upload handling all in one.
Managed to figure it out. Turns out having the notEmpty validation on a file fieldnever works, it always thinks there's nothing there and so always throws that validation message.
Worked around it by writing my own validation method.
I have an upload form that has validation checks whether an item is a valid CSS file or not. I do try to upload a .css file but the validation fails.
Here's the bit where I try to validate the code on my controller:
private function _queueArticle($ref = NULL)
{
$input = Input::all();
$vrules = Config::get('validation.queue_article');
//validate inputs
$validation = Validator::make($input, $vrules);
if ( $validation->fails() ) {
return ( $ref === NULL ) ?
Redirect::to($redir_path)->withErrors($validation)->withInput() :
Redirect::to($redir_path)->withErrors($validation);
}
}
Here's what's $vrules:
'queue_article' => array(
'title' => 'required|max:255',
'body' => 'required',
'slug' => 'unique:articles,slug',
'hero' => 'required|image',
'custom_css' => 'mimes:css'
)
I believe it's failing because it's using PHP's finfo, which is known to fail detecting css not as text/css, but rather text/plain. Doesn't Symfony, Laravel's 'companion code' here (don't know the term), has its own validation class that successfully clears CSS for what it is?
More importantly, what can I do about this?
UPDATE: Error message shows my custom error 'Please upload a valid CSS file.' From app/lang/en/validation.php:
'custom' => array(
'password_old' => array( 'required' => 'Please enter your current password.' ),
'password' => array( 'confirmed' => 'The new passwords don\'t match.' ),
'hero' => array(
'required' => 'A header image is required.',
'image' => 'The header file you included is not valid. Please upload a valid image.'
),
'custom_css' => array('mimes' => 'Please upload a valid CSS file.')
)
UPDATE 2: I checked var_dump($validation) and showed zero contents on array messages and failedRules. It also shows the CSS file having mime type as text/css. All is well, eh, except when I var_dump($validation->fails()) it returns a bool of true.
Additional info will be provided upon request.
Thanks!
I have the follow validation rule for a file:
modelFile.php
public $validate = array(
'image' => array(
'maxWidth' => array(
'rule' => array('maxWidth', 2000),
),
'maxHeight' => array(
'rule' => array('maxHeight', 2000),
),
'extension' => array(
'rule' => array('extension', array('gif', 'jpg', 'png', 'jpeg')),
),
'filesize' => array(
'rule' => array('filesize', 5120000),
)
)
);
Have a way to skip validations, if image are empty?
You may have to adjust how you check if the image is empty/not uploaded - I'm not sure if what I have is correct. But the idea is to check and unset the validation rule.
public function beforeValidate($options = array()) {
if (empty($this->data[$this->alias]['image']['name'])) {
unset($this->validate['image']);
}
return true;
}
See below URL
cakePHP optional validation for file upload
Or try it
"I assign $this->data['Catalog']['image'] = $this->data['Catalog']['imageupload']['name'];"
So by the time you save your data array, it looks something like this I assume:
array(
'image' => 'foobar',
'imageupload' => array(
'name' => 'foobar',
'size' => 1234567,
'error' => 0,
...
)
)
Which means, the imageupload validation rule is trying to work on this data:
array(
'name' => 'foobar',
'size' => 1234567,
'error' => 0,
...
)
I.e. the value it's trying to validate is an array of stuff, not just a string. And that is unlikely to pass the specified validation rule. It's also probably never "empty".
Either you create a custom validation rule that can handle this array, or you need to do some more processing in the controller before you try to validate it
Ok, as far as I know there is no such code to set this in your $validate variable. So what you are going to have to do is:
In the beforeValidate of the corresponding model add the following piece of code:
<?php
# Check if the image is set. If not, unbind the validation rule
# Please note the answer of Abid Hussain below. He says the ['image'] will probably
# never be empty. So perhaps you should make use of a different way to check the variable
if (empty($this->data[$this->alias]['image'])){
unset($this->validate['image']);
}
I used http://bakery.cakephp.org/articles/kiger/2008/12/29/simple-way-to-unbind-validation-set-remaining-rules-to-required as my main article. But this function doesn't seem to be a default cake variable. The code above should work.