yii2 file required custom validation - yii2

I have a form where the required validation rules for the fields can be configures and stored in the model.
When rendering the form, I create the validation rules as follows (simplified) which works fine.
$view_parameters = $competition_article->view_parameters;
if (!empty($view_parameters) && is_array($view_parameters)) {
foreach ($view_parameters as $parameter_key => $parameter_value) {
$field_name = $parameter_value['name'];
$field_required = $parameter_value['required'];
if ($field_required) {
Validator::createValidator('required', $model, [$field_name]);
}
}
}
For the form submission, I use a custom validation rule. This works for all but the file attachment.
public function rules()
{
$rules = [
[['firstname', 'surname', 'closeststore', 'email', 'phone', 'response', 'attachment'],
'dynamicValidator',
'skipOnEmpty' => false
],
[['attachment'], 'file',
'extensions' => 'pdf, jpeg, jpg, doc, docx',
'checkExtensionByMimeType'=>false
],
];
return $rules;
}
On the custom validation method, I handle the file separately.
I tried
a) addError() and return false
b) createValidator() and validateAttribute() pair , which works for the text fields.
public function dynamicValidator($attribute, $params, $validator )
{
$view_parameters = $this->view_parameters;
if ($view_parameters[$attribute]['required'])
$validator = new Validator();
if ($attribute == 'attachment') {
if (empty($_FILES['CompetitionForm']['name']['attachment']))
[$attribute]);
$this->addError( $attribute, 'Please include your attachment to enter.');
// NOTE : Adding the validator has no effect
// $validator = $validator->createValidator('required', $this,
// $validator->validateAttribute($this, $attribute);
return false;
}
$validator = $validator->createValidator('required', $this, [$attribute]);
$validator->validateAttribute($this, $attribute);
}
}
Despite the code being reached, an error is not raised when the attachment is require and the either the addError() or createValidator() is called.
How can I fail the validation when no file is attached and the attachment is required?

You can try with this validation rules for attachment using skipOnEmpty.
[['attachment'], 'file',
'skipOnEmpty' => false,
'extensions' => 'pdf, jpeg, jpg, doc, docx',
'checkExtensionByMimeType'=>false
],

Related

How to add custom new field in yii2 gii generated crud

I have made one CRUD in YII2 using Gii having 10 fields. In my mysql table there was logo column so Gii generated text field for it. I converted it to file field using the following code.
$form->field($model, 'manufacturer_logo')->fileInput()
Now I am not getting any clue where I can write controller side code so that I can save logo in folder and DB like other fields. There is no saving code in Gii generated controller. I tried to follow this but it also did not work.
In my model rules, I have written following line for valid image file.
public function rules()
{
return [
[['manufacturer_description', 'manufacturer_status','manufacturer_logo'], 'string'],
[['manufacturer_name','manufacturer_status','manufacturer_logo'], 'required'],
[['manufacturer_logo'], 'file', 'skipOnEmpty' => true, 'extensions' => 'png, jpg'],
[['created_at', 'updated_at'], 'safe'],
[['manufacturer_name'], 'string', 'max' => 255],
];
}
Now when I fill all fields and browse image as well and press submit then it gives me error "Please upload a file", here you can see so I am not able to upload image and save its name in DB column like other fields.
For further doing RND, Now image is saving in folder but not in database, Here I changed controller code.
public function actionCreate()
{
$model = new Manufacturers();
if ($model->load(Yii::$app->request->post())) {
//upload logo
$base_path = Yii::getAlias('#app');
//$model = new UploadForm();
$imageFile = UploadedFile::getInstance($model,'manufacturer_logo');
$logoName = "";
if (isset($imageFile->size)) {
$imageFile->saveAs($base_path . '/web/uploads/' . $imageFile->baseName . '.' . $imageFile->extension);
$logoName = $imageFile->baseName . '.' . $imageFile->extension;
}
if(trim($logoName)!='') {
$model->manufacturer_logo = trim($logoName);
}
$model->save(false);
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('create', [
'model' => $model,
]);
}

Yii2: Validate content of a file before submitting

In my form I have a field for the user to upload an XML file. But before submitting the XML, I have to validate it. How can I create a validation function for this?
My view:
...
<?= $form->field($model, 'upload_file')->fileInput(['accept' => 'text/xml']) ?>
...
My Model:
...
['upload_file', 'validateFile'],
...
public function validateFile($attribute, $params)
{
// TODO
}
...
I can access and validate the contents of the XML file in the controller, but this validation is already after the file is submitted ... I wanted before submitting!
Exemple in controller:
if ($model->load(Yii::$app->request->post()) {
$file = UploadedFile::getInstance($model, 'upload_file');
$file = file_get_contents($file->tempName);
// xml of the upload_file
$xml = simplexml_load_string($file);
I want to pass this code that is on top, to the validation function.
Add this to your model rules:
public function rules()
{
return [
[['uploaded_file'], 'file', 'skipOnEmpty' => false, 'extensions' => 'xml', 'mimeTypes' => 'text/xml, application/xml'],
];
}

Yii2 POST image to model in API without Yii2 Naming convention

I'm creating an endpoint for a mobile application to send a image to the server. I'm posting the image with the POSTMAN extension for chrome. The image is in the $_FILES variable, and named image. How can I load this image into a model, or the UploadedFile class? The $model->load(Yii::$app->request->post()) line does not correctly load the file, as it is not in Yii2's naming convention for forms.
It's currently returning:
{
"success": false,
"message": "Required parameter 'image' is not set."
}
Code
models\Image.php
<?php
namespace api\modules\v1\models;
use yii\base\Model;
use yii\web\UploadedFile;
class Image extends Model
{
/**
* #var UploadedFile
*/
public $image;
public function rules()
{
return [
[['image'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],
];
}
public function upload()
{
$path = dirname(dirname(__FILE__)) . '/temp/';
if ($this->validate()) {
$this->image->saveAs($path . $this->image->baseName . '.' . $this->image->extension);
return true;
} else {
die(var_dump($this->errors));
return false;
}
}
}
controllers\DefaultController.php
<?php
namespace api\modules\v1\controllers;
use api\modules\v1\models\Image;
use yii\web\Controller;
use yii\web\UploadedFile;
use Yii;
class DefaultController extends Controller
{
public $enableCsrfValidation = false;
public function actionIndex()
{
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$model = new Image();
if (Yii::$app->request->isPost) {
if($model->load(Yii::$app->request->post()))
{
$model->image = UploadedFile::getInstance($model, 'image');
if ($model->upload()) {
// file is uploaded successfully
return ['success' => true, 'message' => 'File saved.'];
}
else return ['success' => false, 'message' => 'Could not save file.'];
}
else return ['success' => false, 'message' => 'Required parameter \'image\' is not set.'];
}
else return ['success' => false, 'message' => 'Not a POST request.'];
}
}
Postman
Your problem seems to be the name you are using to send the image file. Usually Yii2 uses names for form attributes like "ModelName[attributeName]" and you are sending your image file with the name "image"
There are 2 ways of fixing this:
Change the name you use to send your image file to follow the same naming conveniton. However you don't seem to want that.
Use getInstanceByName('image') method instead of getInstance($model, 'image')
The problem come here
When you send files via api they are not sent asynchronously. If you check
echo '<pre>';
print_r($_FILES); //returns nothing
print_r($_POST["image"]); //returns something
echo '</pre>';
die;
One reason is that your controller extendsyii\web\controller which is not used by rest apis, extend yii\rest\controller
The other way to go about this is by using javascript formData when sending the post request
This is a way i handled a previous ajax post of an image probably itll give you a guideline
The form
<?php $form = ActiveForm::begin(['options' => ['enctype' =>
'multipart/form-data','id'=>'slider_form']]); ?> //dont forget enctype
<?= $form->field($model, 'file')->fileInput() ?>
Then on the ajax post
var formData = new FormData($('form#slider_form')[0].files);
$.post(
href, //serialize Yii2 form
{other atributes ,formData:formData}
)
Then on the controller simply access via
$model->file =$_FILES["TblSlider"]; //here this depends on your form attributes check with var_dump($_FILES)
$file_tmp = $_FILES["TblSlider"]["tmp_name"]["file"];
$file_ext = pathinfo($_FILES['TblSlider']['name']["file"], PATHINFO_EXTENSION);
if(!empty($model->file)){
$filename = strtotime(date("Y-m-d h:m:s")).".".$file_ext;
move_uploaded_file($file_tmp, "../uploads/siteimages/slider/".$filename);
///move_uploaded_file($file_tmp, Yii::getAlias("#uploads/siteimages/slider/").$filename);
$model->image = $filename;
}
I hope this helps

Custom Yii2 validator not firing

I have custom form where I am trying to write a custom validator, but its not firing. The model is returned as valid every time submit button is hit:
class DeactivateForm extends Model {
public $deactivate_reason;
public function rules() {
return [
[ 'deactivate_reason', 'reasonValidator' ],
];
}
public function reasonValidator( $attribute, $params ) {
$this->addError( 'deactivate_reason', 'Error !!!' );
}
public function attributeLabels() {
return [
'deactivate_reason' => 'Reason for deactivating',
];
}
}
The actual form is plain jane:
$form = ActiveForm::begin( [
'id' => 'deactivate-form'
] );
When using [ 'deactivate_reason', 'required' ] in the rules, the required rule works fine, custom rule is still ignored...
I am not sure but to forcefully run validation on empty field, add following property.
skipOnError=>false and skipOnEmpty=>false
[
['deactivate_reason', 'reasonValidator', 'skipOnError' => false,'skipOnEmpty'=>false],
]
Try this,add return like below
public function reasonValidator( $attribute, $params ) {
return $this->addError( 'deactivate_reason', 'Error !!!' );
}

yii2 How to transfer post data from one view to two?

I am trying to create make a two-step form in yii2.
This is my SiteController.php
public function actionCreateCharacter()
{
$model = new Character();
var_dump(Yii::$app->request->post('Character'));
if ($model->load(Yii::$app->request->post())) {
$attributes=['imie','nazwisko','plec','wyznanie_id'];
if ($step1 = $model->validate($attributes)) {
//var_dump($step1);
// form inputs are valid, do something here
//var_dump(Yii::$app->request->post('Character');
return $this->render('createCharacterStep2', [
'model' => $model,
]);;
}
else {
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}
}
return $this->render('createCharacter', [
'model' => $model,
]);
}
public function actionCreateCharacterStep2()
{
$model2 = new Character();
var_dump($model);
if ($model2->load(Yii::$app->request->post())) {
var_dump(Yii::$app->request->post('Character'));
if ($model2->validate()) {
// form inputs are valid, do something here
return;
}
}
/*return $this->render('createCharacter2', [
'model' => $model,
]);*/
}
... and this is my Character.php (model + attributeLabels and tableName)
public function rules()
{
return [
[['user_id', 'imie', 'nazwisko', 'plec', 'wyznanie_id', 'avatar_src', 'avatar_svg'], 'required'],
[['user_id', 'wyznanie_id'], 'integer'],
[['avatar_svg'], 'string'],
[['imie'], 'string', 'max' => 15],
[['nazwisko'], 'string', 'max' => 20],
[['plec'], 'string', 'max' => 1],
[['avatar_src'], 'string', 'max' => 30]
];
}
I have access to $_POST by Yii::$app->request->post() in createCharacter - I get imie, nazwisko, plec and wyznanie_id.
But when I send the form in step 2 I have only post data from step 2.
How can I set the post data from step1+step2?
Sorry for my english and thanks in advance.
While rendering step2 from step1 action, you can always pass additional data to controller's action. So I added "STEPONEPOSTS" post variable which contains all posts of step 1. Check below.
public function actionCreateCharacter()
{
$model = new Character();
var_dump(Yii::$app->request->post('Character'));
if ($model->load(Yii::$app->request->post())) {
$attributes=['imie','nazwisko','plec','wyznanie_id'];
if ($step1 = $model->validate($attributes)) {
//var_dump($step1);
// form inputs are valid, do something here
//var_dump(Yii::$app->request->post('Character');
return $this->render('createCharacterStep2', [
'model' => $model,
'STEPONEPOSTS' => Yii::$app->request->post(),
]);;
}
else {
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}
}
return $this->render('createCharacter', [
'model' => $model,
]);
}
And now in step 2 view, you can get step 1 posts variable as
$STEPONEPOSTS
There is another way , if you have to table for step 1 and step 2. then save the data of step 1 first then step2 data. if you are not using two tables then you can create two form each form for each step and also create scenarios for each step according to the fields.I think this may help . You can use session also as per discussion in comments or you can use the extension array wizard but array wizard extension is not well documented , so i suggest you try my way i will help you.