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!
Related
I'm using CI 3.1.7 and want to stop validating form if there is an error. For example:
$config = array(
array(
'field' => 'username',
'label' => 'Username',
'rules' => 'required',
'errors' => array(
'required' => '%s is required',
),
),
array(
'field' => 'password',
'label' => 'Password',
'rules' => 'required',
'errors' => array(
'required' => '%s is required',
),
),
array(
'field' => 'email',
'label' => 'Email',
'rules' => 'required',
'errors' => array(
'required' => '%s is required',
),
)
);
When user leaves username and email blank, the form will show only username is required. Any help is appreciated, thank you!
You cannot stop validation->run() but you can control which error message displays. The limitation is you cannot show the error next to the related field. Or, I should say instead, I cannot think of an easy way to show the error next to the input it belongs to.
Anyway, here's how to extract the first error message.
if($this->form_validation->run() == FALSE)
{
$errors = $this->form_validation->error_array();
// There could be many but grab only the first
$fields = array_keys($errors);
$err_msg = $errors[$fields[0]];
}
If you want the name of the field you can use this.
$err_field = $fields[0];
If i got your question right .. you want to stop on the first encountered form validation error then you have to edit the core form validation library which is extremely bad practice but luckily you can extend its functionality and either edit the run() method itself or create your own method and just copy the run() code and edit this block of code:
// Execute validation rules
foreach ($this->_field_data as $field => &$row)
{
// Don't try to validate if we have no rules set
if (empty($row['rules']))
{
continue;
}
$this->_execute($row, $row['rules'], $row['postdata']);
// here is the modification
if(count($this->_error_array) > 0) return true; // error found
}
now it will stop execution when finding its first error
Pleas check
public function __construct()
{
parent::__construct();
// load form and url helpers
$this->load->helper(array('form', 'url'));
// load form_validation library
$this->load->library('form_validation');
}
Or check https://code.tutsplus.com/tutorials/codeigniter-form-validation-from-start-to-finish--cms-28768
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 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 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.
I wanted to incorporate GUMP https://github.com/Wixel/GUMP into my site for server side validation. But unfortunately the documentation is light and I am new to PHP.
Here is my validation code:
//Validation
$gump = new GUMP();
$rules = array(
'dept' => 'required|numeric',
'hosp' => 'required|numeric',
'subhosp' => 'required|numeric',
'user' => 'required|numeric',
'gpo' => 'required|boolean|exact_len,1',
'clin' => 'required|valid_name',
'clinmail' => 'required|valid_email',
'comp' => 'required|alpha_dash',
'cpt' => 'required|alpha_dash',
'past' => 'required|boolean|exact_len,1',
'latex' => 'required|boolean|exact_len,1',
);
$validated = $gump->validate($_POST,$rules);
print_r($validated); // Something went wrong
The output from the above code gives me an array like so when I am looking at my AJAX response in FireBug:
Array
(
[0] => Array
(
[field] => clin
[value] => .-0
[rule] => validate_valid_name
)
[1] => Array
(
[field] => clinmail
[value] => %$sd
[rule] => validate_valid_email
)
)
And I what I need is something like so:
<div class="error-msg">You did not enter a valid email address</div><br>
<div class="error-msg">You did not enter a valid username</div><br>
From the documentation I get:
if($validated === TRUE)
{
// Do something, everything went well
}
else
{
// This is where I am stuck. Do I have to loop through and put my <div> tags here?
}
My question is how would the community handle outputting error messages with this class? My only thought is that I loop through the results above and output different messages depending on the field and the rule that was broken but that seems tedious. Is there a better way or a better class to use that does standalone PHP input validation? I was using another very easy to use class but it started breaking when I moved away from inline PHP to AJAX.
edit your code to:
$gump = new GUMP();
$rules = array(
'dept' => 'required|numeric',
'hosp' => 'required|numeric',
'subhosp' => 'required|numeric',
'user' => 'required|numeric',
'gpo' => 'required|boolean|exact_len,1',
'clin' => 'required|valid_name',
'clinmail' => 'required|valid_email',
'comp' => 'required|alpha_dash',
'cpt' => 'required|alpha_dash',
'past' => 'required|boolean|exact_len,1',
'latex' => 'required|boolean|exact_len,1',
);
$error_texts = array(
'dept' => 'You must enter a numeric value',
'hosp' => 'You must enter a numeric value',
'subhosp' => 'You must enter a numeric value',
'user' => 'You must enter a numeric value',
'gpo' => 'You must enter a boolean value',
'clin' => 'You must enter a valid name',
'clinmail' => 'You must enter a valid email',
'comp' => 'You must enter a valid alpha dash',
'cpt' => 'You must enter a valid alpha dash',
'past' => 'You must enter 1 char',
'latex' => 'You must enter 1 char',
);
$validated = $gump->validate($_POST,$rules);
if($validated === TRUE)
{
echo "Every thing is ok";
}
else
{
foreach($validated as $key=>$error)
{
echo '<div class="error-msg">' . $error_texts["{$error['field']}"] . '</div><br />';
}
}
I would like to add that if a validation fails twice, for example if a value is required and has to exist out of more than 10 characters then #semsems answer would print multiple lines for the same field.
I changed the above code and added:
$_validated = array();
foreach($validated as $key=>$error)
{
if ( !in_array($error['field'], $_validated) )
{
print '<div class="error-msg">' . $error_texts["{$error['field']}"] . '</div>';
$_validated[] = $error['field'];
}
}
I know this question is a year old and been answered, but since I based myself on the accepted answer and in my view made it a sweet better (completely generic with locale as extra), I thought it would be nice to share and get some feedback... what do you guys think about my code? Now based on semsem's answer... I like the way he coded part of it and I derived from it to come up with the following code:
First I extended GUMP with my own class so I could overwrite some functions
<?php
require("gump.class.php");
class GumpValidator extends GUMP
{
private $locale;
private $translator;
public function GumpValidator($lang = "en")
{
$this->locale = $lang;
$this->loadValidationLocales();
}
/** Overwrite the default validate() function of GUMP so that I can add an extra "message" property */
public function validate(array $input, array $ruleset)
{
$validated_data = GUMP::validate($input, $ruleset);
if(is_array($validated_data)) {
foreach($validated_data as $index=>$error) {
$validation = str_replace(":attribute", $error['field'], $this->translator[$error['rule']]);
$validation = str_replace(":param", $error['param'], $validation);
$validated_data[$index++]['message'] = $validation;
}
}
return $validated_data;
}
/** Depending on the language locale, load the proper set of validation messages */
private function loadValidationLocales()
{
$this->translator = require "/lang/" . $this->locale . "/validation.php";
return $this->translator;
}
} // EOC
Then as you can see from the extended class, I created myself a set of locale language message for validation and I took the full list of possible validation errors directly from the GUMP code on Github, take a look at the function get_readable_errors() to get the full list of errors. Github link here... The locales which I created are saved under separate language folders /lang/en/validation.php, /lang/fr/validation.php, etc... You can easily create as many as you want... I took the concept from Laravel Framework... These validation.php files looks for example, like this (1 file per language/locale), here is the English one:
<?php
return array(
"validate_alpha" => "The :attribute may only contain letters.",
"validate_alpha_numeric" => "The :attribute may only contain letters and numbers.",
.....
);
Then finally in my POST to validate code I just call it like this:
// load my extended class with language locale I want to use
$gump = new GumpValidator("en");
// now validate the data
$validated_data = $gump->validate( $_POST, $rules );
So at the end I get the extra property of message inside the $validated_data object returned with the option of whichever locale language I chose to display the error message...and voilĂ !!! In my case I read the code in javascript to display the errors in Bootstrap alert, but if you want PHP you can look at semsem answer as well. So at the end, I know it takes more code since I have an extra class to wrap it and extra locales, but isn't sweet to first have it generic (per validation errors) and second to have multiple locale language easily?