The problem is if I want to update the other fields and not the image. It passes the validation and doesn't update any of the fields.
But if I update the image and other fields it updates. Or if i update the image it updates.
View:
<?php echo $form->labelEx($model,'pimg'); ?>
<?php echo $form->fileField($model, 'pimg',array('id'=>'imgInput',)); ?>
<?php echo $form->error($model,'pimg'); ?>
Controller:
public function actionEdit($id)
{
$model=$this->loadModel($id);
if(isset($_POST['Product']))
{
$model->pimg=CUploadedFile::getInstance($model,'pimg');
$fileName=$model->pimg;
$model->attributes=$_POST['Product'];
if($model->save())
$model->pimg->saveAs('images/'.$fileName);
$this->redirect(array('display','id'=>$model->productid));
}
$this->render('edit',array('model'=>$model,));
}
Model rules:
array('name, category, model, brand, description, price', 'required'),
array('pimg', 'file','types'=>'jpg','on'=>'create', 'allowEmpty'=>false),
array('pimg', 'file','types'=>'jpg','on'=>'update', 'allowEmpty'=>true),
I think the problem is with the controller. I keep getting the error:
Fatal error: Call to a member function saveAs() on a non-object in D:\wamp\www\testfolder\protected\controllers\ProductController.php on line 147
line 147: $model->pimg->saveAs('images/'.$fileName);
Image appears but then image name from db doesn't appear next to the choose file button renders stating no file chosen.
Note that I am new to Yii, and am stuck with this.
The problem is in your actionEdit i.e here
$model->pimg=CUploadedFile::getInstance($model,'pimg');
$fileName=$model->pimg;
$model->attributes=$_POST['Product'];
if($model->save())
$model->pimg->saveAs('images/'.$fileName);
here you should check if the image has been uploaded or not. what happens is that yii is unable to execute this line $model->pimg->saveAs('images/'.$fileName); as no image has been uploaded. so you need to wrap it inside a condition i.e
if(!empty($model->pimg))
{
$model->pimg->saveAs('images/'.$fileName);
}
so your final code should be something like this
$model->attributes=$_POST['Product'];
$model->pimg=CUploadedFile::getInstance($model,'pimg');
if(!empty($model->pimg))
{
$fileName=$model->pimg->name;
$model->pimg->saveAs('images/'.$fileName);
}
if($model->save())
//render here
Update 1
If you want that on update null should not be stored in your database then you can do this
$model=$this->loadModel($id);
$prevImage=$model->pimg; //add this line
and then where you are using this
if(!empty($model->pimg))
{
$model->pimg->saveAs('images/'.$fileName);
}
else{
$model->pimg=$prevIMage;
}
add this else code
Related
I am using Yii2 framwork. My file upload function worked well when I upload a single img, while when I click the posed article and I only want to update the post again(Suppose I didn't want to update the img, I only want to update other contents). But I found my uploaded file were replaced with an empty value(varchar type) when I click view. my uploaded img can't show out.
I do tried to fixed by myself as below, But the existing file value can't be saved when I click the submit button.
<?php
if (($post->file) != "") {
echo $form->field($post, 'file')->textInput()->hint('My currently uploaded file')->label('My Photo') ;
}
else{
echo $form->field($post, 'file')->fileInput()->hint('To upload a new file')->label('My Photo') ;
}
?>
When I click submit button, my existing file was gone.
Is there any good way to fix it.
Thanks for your opinions in advance.
Use another variable in your model for upload a file.
For example use file_field name for get file from submitted and store in file field.
class PostModel extends Model
{
/**
*
* #var UploadedFile
*/
public $file_field;
public function rules() {
return [
['file_field', 'file'],
];
}
}
echo $form->field($post, 'file_field')->fileInput()->hint('To upload a new file')->label('My Photo') ;
$post->file_field = UploadedFile::getInstance($post, 'file_field');
For upload new file check the file_field:
if ($post->file_field) {
// $post->file old file
// Save $post->file_field and store name in $post->file
}
Add a rule to your model rules:
[['file'], 'file', 'skipOnEmpty' => true, 'extensions' => 'png, jpg'],
and check for an empty upload in your controller:
if (Yii::$app->request->isPost) {
$ok = true;
// process your other fields...
...
// process image file only if there is one
$post->file= UploadedFile::getInstance($post, 'file');
if ($post->file && $post->upload()) {
}
if ($ok) {
return $this->redirect(...);
}
}
See Yii2 docs and the Yii2 guide for detailed infos about file upload.
I have created a dependent dropdown that shows delivery time for selected area,by default it will show aprompt which i have given at the view page but when i choose an area it directly displays the values with first value as selected ,how can i give a prompt instead the first value get selected .here my action
public function actionGetdeliveryforarea()
{
$data=Areatimeslot::model()->findAll('area_id=:area_id',
array(':area_id'=>(int) $_POST['area_id']));
$data=CHtml::listData($data,'deliverytime','timeName');
foreach($data as $value=>$deliverytime)
{
echo CHtml::tag('option',
array('value'=>$value,),CHtml::encode($deliverytime),true);
}
}
Update your action as follows
public function actionGetdeliveryforarea()
{
$data=Areatimeslot::model()->findAll('area_id=:area_id',
array(':area_id'=>(int) $_POST['area_id']));
$data=CHtml::listData($data,'deliverytime','timeName');
echo CHtml::tag('option',
array('value'=>'',),CHtml::encode("Select Value"),true);
foreach($data as $value=>$deliverytime)
{
echo CHtml::tag('option',
array('value'=>$value,),CHtml::encode($deliverytime),true);
}
}
I'm basically trying to create a multi-step form using the CActiveForm class in Yii. The idea is I want to use the built-in functionality to achieve this in the simplest way possible. The requirement I have is as follows:
A multi step ONE PAGE form (using DIVs that show/hide with jQuery)
AJAX validation on EACH step (validate step-specific attributes only)
The validation MUST work using the validateOnChange() and validateOnSubmit() methods
This is a half-working solution I have developed so far:
View:
<div class="form">
<?php $form = $this->beginWidget('CActiveForm', array(
'id'=>'listing-form',
'enableClientValidation'=>false,
'enableAjaxValidation'=>true,
'clientOptions'=>array(
'validateOnChange'=>true,
'validateOnSubmit'=>true,
'afterValidate'=>'js:validateListing',
),
)); ?>
<?php echo $form->errorSummary($model); ?>
<div class="step" id="step-1">
// model input fields
<?php echo CHtml::submitButton('Next Step', array('name'=>'step1')); ?>
</div>
<div class="step" id="step-2" style="display: none;">
// model input fields
<?php echo CHtml::submitButton('Next Step', array('name'=>'step2')); ?>
</div>
<div class="step" id="step-3" style="display: none;">
// model input fields
<?php echo CHtml::submitButton('Submit', array('name'=>'step3')); ?>
</div>
<?php $this->endWidget(); ?>
</div>
JavaScript:
function validateListing(form, data, hasError)
{
if(hasError)
{
// display JS flash message
}
else
{
if($('#step-1').css('display') != 'none')
{
$('#step-1').hide();
$('#step-2').show();
}
else if($('#step-2').css('display') != 'none')
{
$('#step-2').hide();
$('#step-3').show();
}
else if($('#step-3').css('display') != 'none')
{
return true; // trigger default form submit
}
}
}
Controller:
public function actionCreate()
{
$model = new Listing;
// step 1 ajax validation
if(isset($_POST['step1']))
{
$attributes = array('name', 'address1', 'etc');
$this->performAjaxValidation($model, $attributes);
}
// step 2 ajax validation
if(isset($_POST['step2']))
{
$attributes = array('category', 'type', 'etc');
$this->performAjaxValidation($model, $attributes);
}
// step 3 ajax validation
if(isset($_POST['step3']))
{
$attributes = array('details', 'source', 'etc');
$this->performAjaxValidation($model, $attributes);
}
// process regular POST
if(isset($_POST['Listing']))
{
$model->attributes = $_POST['Listing'];
if($model->validate()) // validate all attributes again to be sure
{
// perform save actions, redirect, etc
}
}
$this->render('create', array(
'model'=>$model,
));
}
protected function performAjaxValidation($model, $attributes=null)
{
if(isset($_POST['ajax']) && $_POST['ajax']==='listing-form')
{
echo CActiveForm::validate($model, $attributes);
Yii::app()->end();
}
}
To summarise. Basically what I have is a form with 3 submit buttons (one for each step). In my controller I check which submit button was pressed and I run AJAX validation for the attributes specific to that step.
I use a custom afterValidate() function to show/hide the steps upon submit. On step 3, the default form submit is triggered, which posts all the form attributes to the controller.
This works well, except it won't work with validateOnChange() (since the submit button doesn't get posted). Also I was wondering whether this is actually the best way to do this, or if anyone knows of a better way?
Thanks.
I'd suggesting using scenarios to turn on and off the appropriate rules. Adjust the model scenario based on what is sent to your controller.
Note: this may also be a really good place to use a CFormModel instead of a CActiveRecord, depending on what is in your form.
Edit: can you add a hidden field to each div section that contains the info about what step you are on? Seems like that should work instead of your submit buttons.
OPTION 1
When you do not receive a button, why not validate the entire form, why do you need to validate only specific attributes? Yii will validate the entire model, send back all the errors but only that particular error will be shown by the active form because that is how it works already.
OPTION 2
You can have 3 forms (not 1 like you have now), 1 on each step. Also create 3 scenarios 1 for each step.
Each form has a hidden field that gets posted with the form, it can actually be the scenario name just validate it when it comes in. Validate the model using this hidden field to set the scenario you are on.
You can cache parts on the model when the form is submitted successfully and at the end you have the complete model.
you can always have custom validation and it won't break your normal form validation
in your model
private $step1 = false;
private $step2 = false;
private $all_ok = false;
protected function beforeValidate()
{
if(!empty($this->attr1) && $this->attr2) // if the fields you are looking for are filled, let it go to next
{
$this->step1 = true;
}
if($this->step1)
{
... some more validation
$this->step2 = true;
}
if($this->step2)
{
... if all your logic meets
$this->all_ok = true;
}
// if all fields that your looking for are filled, let parent validate them all
// if they don't go with their original rules, parent will notify
if($this->all_ok)
return parent::beforeValidate();
$this->addError($this->tableSchema->primaryKey, 'please fillout the form correctly');
return false;
}
I think better create specific class for each step of validation and use scenarios with rules. Below is small example.
//protected/extensions/validators
class StepOneMyModelValidator extends CValidator
{
/**
* #inheritdoc
*/
protected function validateAttribute($object, $attribute)
{
/* #var $object YourModel */
// validation step 1 here.
if (exist_problems) {
$object->addError($attribute, 'step1 is failed');
}
...
Create other classes(steps) for validation...
// in your model
public function rules()
{
return array(
array('attr', 'ext.validators.StepOneMyModelValidator', 'on' => 'step1'),
...
How to use in controller:
$model = new Listing();
$steps = array('step1', 'step2', /* etc... */);
foreach($_POST as $key => $val) {
if (in_array($key, $steps)) {
$model->setScenario($key);
break;
}
}
$model->validate();
echo '<pre>';
print_r($model->getErrors());
echo '</pre>';
die();
Or we can validate all steps in one validator.
i want to keep the photo column for the registration page so that user can select the image and display in that column for this i am using ImageSelect extension in yii framework but it is not working suggest me how to keep the image while registering the user
Here is my code for ImageSelect
<?php echo $form->labelEx($model,'profileImage'); ?>
<?php
$this->widget('ext.imageSelect.ImageSelect', array(
'path'=>Yii::app()->baseUrl . '/images/Penguins.jpg',
'alt'=>'alt text',
'uploadUrl'=> 'profileinfo/upload',
'htmlOptions'=>array()
));
?>
my controller code is
public function actionUpload()
{
$file = CUploadedFile::getInstanceByName('file');
// Do your business ... save on file system for example,
// and/or do some db operations for example
$file->saveAs(Yii::app()->baseUrl . '/images/'.$file->getName());
// return the new file path
echo Yii::app()->baseUrl.'/images/'.$file->getName();
}
I am working with the Yii framework.
I have set a value in one of my cgridview filter fields using:
Here is my jQuery to assign a value to the searchfield:
$('#gridviewid').find('input[type=text],textarea,select').filter(':visible:first').val('".$_GET['value']."');
And here my PHP for calling the cgridview:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'bills-grid',
'dataProvider'=>$dataProvider,
'filter'=>$model,
'cssFile'=>Yii::app()->baseUrl . '/css/gridview.css',
'pager'=>array(
'class'=>'AjaxList',
'maxButtonCount'=>25,
'header'=>''
),
'columns' => $dialog->columns(),
'template'=>"<div class=\"tools\">".$dialog->link()." ".CHtml::link($xcel.' Export to excel', array('ExcelAll'))."</div><br />{items}{summary}<div class=\"pager-fix\">{pager}</div>",));
The value appears in the search field and my cgridview works correctly without any issues, but I am unable to trigger the cgridview to refresh or filter. Does anyone know who to trigger the cgridview to filter after page load with a predefined value?
Any help would be greatly appreciated and please let me know if you need additional information.
Thank you.
You can solve the problem without any clientside code modification. In your controller action just set the default value for the attribute as shown below
public function actionAdmin()
{
$model = new Bills();
$model->unsetAttributes();
$model->attribute_name="default filter value";//where attribute_name is the attribute for which you want the default value in the filter search field
if(isset($_GET['Bills'])){
$model->attributes = $_GET['Bills'];
}
$this->render('admin',array('model'=>$model));
}
Have a look at 'default' index action that gii generates:
public function actionIndex()
{
$model = new Bills();
$model->unsetAttributes();
if(isset($_GET['Bills'])){
$model->attributes = $_GET['Bills'];
}
$this->render('index',array('model'=>$model));
}
So if you add one line like: $model->attribute = 'test';, you're done. 'attribute' is of course the attribute that has to have the default filter value (in this case value is 'test') :). So your code looks like:
public function actionIndex()
{
$model = new Bills();
$model->unsetAttributes();
if(isset($_GET['Bills'])){
$model->attributes = $_GET['Bills'];
}
if(!isset($_GET['Bills']['attribute']) {
$model->attribute = 'test';
}
$this->render('index',array('model'=>$model));
}
Of course youre attribute will have a test value (in filter) set up as long as you wont type anything in its filter field. I hope that that's what you're looking for. Your filter should work as always.
Sorry for my bad english :)
Regards
You can use Yii's update:
$.fn.yiiGridView.update('bills-grid', {
type: 'GET',
url: <?php echo Yii::app()->createUrl('controller/action') ?>"?Class[attribute]=<?php echo $_GET['value'] ?>
success: function() {
$.fn.yiiGridView.update('bills-grid');
}
});
This is how i do it, just change the URL, it should be the same controller action of the gridview and change URL parameters to the structure represented in there, should be like Bills[attribute]=value.