Value not updated in databases after validation and save success - php

Hi everyone i'm having trouble with my software developed with yii2.
I Have a model called Anagrafica and with its primary key id. With this model everything works.
I also have a model called AnagraficaOpzioniCarriera which extend the first one.
I have a view anagrafica/index that show a Kartik grid with the data of people enrolled that you can find in anagrafica. Admin user can update the data of an Anagrafica model by clicking on an the attribute "cognome" that render to anagrafica/update.
this is the command that call the controller AnagraficaController to reach anagrafica/update
'cognome'=>Grid::Labels('cognome',['anagrafica/update'],\app\helpers\Permits::allow('anagrafica','update'),'id','btn','info','10%'),
This is AnagraficaController
public function actionUpdate($id,$error=0,$message='')
{
$id = (int)$id;
$model = Anagrafica::findOne(['id' => $id]);
$model->scenario = 'update';
if ($model->load(Yii::$app->request->post())) {
if($model->validate()){
}
if($model->save(false)){
return $this->redirect(['anagrafica/update','id'=>$model->id]);
}
}
}
return $this->render('update', ['model' => $model, 'extended'=>true]);
}
i removed some portions of code to semplify it, but this is the core.
One time the view anagrafica/update is reached in this page i have an ActiveForm to modify data of the model and i have a render to a grid that show the attributes contained in AnagraficaOpzioniCarriera about the $model that i'm updating.
<?= $this->render('_opzioni_carriera',['parent'=>$model]); ?>
anagrafica/_opzioni_carriera view contain a Kartik grid that shows the column in the model AnagraficaOpzioniCarriera
<?php
use kartik\grid\GridView;
use kartik\select2\Select2;
use kartik\widgets\ActiveForm;
use kartik\editable\Editable;
use kartik\widgets\SwitchInput;
use yii\helpers\ArrayHelper;
use app\helpers\Autoconfigurazione;
use app\models\AnagraficaOpzioniCarriera;
use app\helpers\Grid;
use yii\helpers\Html;
use app\helpers\UserInfo;
/* #var $this yii\web\View */
/* #var $model app\models\AnagraficaOpzioniCarriera*/
$model = new AnagraficaOpzioniCarriera(['scenario'=>'search']);
?>
<div class="">
<?php
echo GridView::widget([
'options'=>[
'id'=>'opzioni_carriera',
],
'dataProvider'=> $model->search($parent->id,Yii::$app->request->queryParams),
'showPageSummary'=>false,
'headerRowOptions'=>['class'=>'kartik-sheet-style'],
'pjax'=>true, // pjax is set to always true for this demo
'pjaxSettings'=>[
'neverTimeout'=>true,
],
'toolbar'=> [
[
'content'=>''
],
],
'panel'=>[
'heading'=>false,
'footer'=>false,
'after'=>false,
],
'columns' => Grid::gridColumns([
'model'=>$model,
'checkbox'=>false,
'remove'=>Grid::gridRemove($model),
'extraOptions' =>[
'cashback' =>Grid::YNColumn('cashback',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
'compensa'=>Grid::YNColumn('compensa',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
'associazione'=>Grid::YNColumn('associazione',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
'formazione'=>Grid::YNColumn('formazione',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
],
]);
?>
</div>
cashback, compensa etc.. are the attributes in the model AnagraficaOpzioniCarriera.
Here when i try to update this attributes everything looks fine, the function model->validate() and model->load returns true value, but at the end of the process doesn't works.
Honestly i don't know what i have to return from the function of the controller.
public function actionUpdate($id)
{
$model = AnagraficaOpzioniCarriera::findOne(['id_anagrafica' => $id]);
if (!$model) {
// Se l'anagrafica opzioni carriera non esiste, genera un'eccezione 404
throw new \yii\web\NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
}
$model->scenario = 'update';
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
if(Yii::$app->request->post('cashback') != null) $model->cashback = Yii::$app->request->post('cashback');
if(Yii::$app->request->post('compensa') != null) $model->cashback = Yii::$app->request->post('compensa');
if(Yii::$app->request->post('associazione') != null) $model->cashback = Yii::$app->request->post('associazione');
if(Yii::$app->request->post('formazione') != null) $model->cashback = Yii::$app->request->post('formazione');
if ($model->save()) {
return Json::encode(["success" => true, 'message' => 'Dati aggiornati']);
}
}
// Mostra il form di modifica
return $this->render('_opzioni_carriera', [
'parent' => $model,
]);
}
anyone can help me? i hope i explained my problem in a good form, but my english is not the best, i know. Anyway thanks in aadvance to everyone who want to try to help me, if you need anything other you can easily ask.
I tried every everything, also a logger but nothing worked
Like someone suggest these are the rules of the model AnagraficaOpzioni, but like i said prevously model->validate() works, for this reason i think the problem is not over there
public function rules()
{
return [
[['id_anagrafica'], 'required'],
[['id_anagrafica'], 'integer'],
[['cashback', 'compensa', 'associazione', 'formazione'], 'required', 'on'=>['update']],
[['cashback', 'compensa', 'associazione', 'formazione'], 'integer'],
[['id_anagrafica', 'cashback', 'compensa', 'associazione', 'formazione',], 'safe', 'on'=>['search']],
];
}

Related

Saving data to database using a modal

I have a problem.
I need to open a modal to insert data in a table in the form of another table.
I can open a modal using Ajax with the view of the create view using the actionCreate from the controller, but when i clic save in the modal, the controller redirect me to the action view by default, and if i chage that, it open the full view in the current tab.
The problem was temporary resolved by making a redirect to the view that the modal should be in the if condition, but when i test to insert invalid data, the view render full-windowed with the message errors (in ajax render).
Also, the data always is save.
I know there's a way to render it in the modal without change view, but i'm new and not have any experience working with modals.
Need your help please.
There's my view file:
<?php
use yii\helpers\Html;
use yii\helpers\ArrayHelper;
use yii\widgets\ActiveForm;
use yii\bootstrap\Modal;
use yii\helpers\Url;
use app\models\Catalogo;
/* #var $this yii\web\View */
/* #var $model app\models\Articulo */
/* #var $form yii\widgets\ActiveForm */
?>
<div class="articulo-form">
<?php $form = ActiveForm::begin(['id'=>'articuloCreate']); ?>
<?= $form->field($model, 'ID_CATALOGO')->dropDownList(
ArrayHelper::map(Catalogo::find()->all(),'ID_CATALOGO','NOMBRE_CATALOGO'),
[
'style'=>'width:500px',
'prompt'=>'Seleccionar catálogo',
]); ?>
<?= Html::button('Agregar catálogo', [
'value' => Url::to(['catalogo/create']),
'class' => 'btn btn-primary',
'id' => 'BtnModalCatalogo',
'data-toggle'=> 'modal',
'data-target'=> '#catalogo',
]) ?>
<?php ActiveForm::end(); ?>
The modal in the same view:
<?php
Modal::begin([
'header' => 'Crear catálogo',
'id' => 'catalogo',
'size' => 'modal-md',
]);
echo "<div id='modalContent'>BtnModalCatalogo</div>";
Modal::end();
?>
There's more code, but i think is pointless putting it.
This is my JS:
$('#BtnModalCatalogo').click(function(e){
e.preventDefault();
$('#catalogo').modal('show')
.find('#modalContent')
.load($(this).attr('value'));
return false;
});
And this is the action create in the controller:
public function actionCreate()
{
$model = new Catalogo();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->ID_CATALOGO]);
}
if(Yii::$app->request->isAjax){
return $this->renderAjax('create', [
'model' => $model,
]);
}
return $this->render('create', [
'model' => $model,
]);
}
Just in case, this is the action view:
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
First of all, thanks.
At the beggining, i expectet that the controller should load any other views in the modal, but not was like that.
This action only responds when the action is called via ajax and will only redirect if the model is saved.
public function actionCreate()
{
$model = new Catalogo();
if ($model->load(Yii::$app->request->post())) {
if($model->save()) return $this->redirect(['view', 'id' => $model->ID_CATALOGO]);
}
return $this->renderAjax('create', [
'model' => $model,
]);
}

I already asked , but how to access view page without id value in the url

my view.php
<?php
echo
Html::beginForm(['contactpersons/update'], 'post',['id' => 'update-form']) .
'<input type="hidden" name="id" value="'.$model->id.'">
<a href="javascript:{}" onclick="document.getElementById(\'update-form\').submit();
return false;">Update</a>'.
Html::endForm();
?>
<?= Html::a('Delete', ['delete', 'id' => $model->id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => 'Are you sure you want to delete this item?',
'method' => 'post',
],
])
?>
my controller is
public function actionView($id)
{
$model = $this->findModel($id);
return $this->render('view', ['model' => $model]);
}
How to modify this for getting my view page without id value in the url.
Thanks in advance.
You could change it like this
public function actionView($id = null) {
$model = null;
if ($id !== null) {
$model = $this->findModel($id);
}
return $this->render('view', ['model' => $model]);
}
However in your view you execute the following code: $model->id
this won't work when the model isn't set yo anything. So you could create a new model ($model = new ModelClass()) when the $id is null.
Sidenote: this doesn't look like an view action but more like an edit action, so maybe change your action to actionEdit().
You can send data to your browser by two methods - POST and GET. So, if you want to hide id parameter, then you need to send your id as POST parameter, which is bad solution - it's hard to implement, because POST is usally sends when you submit a form.

Yii2 multiple models in one form

How to use multiple models in one form in Yii2?
My case:
In my create action I can save into agenda_fiscalizacao table, but in update I receive this error when I try to load the form:
Call to a member function formName() on array
My Update Action:
public function actionUpdate($id)
{
$model = $this->findModel($id);
$modelAgenda = AgendaFiscalizacao::findAll(['fiscalizacao_id' => $id]);
if ($model->load(Yii::$app->request->post()) && Model::loadMultiple($modelAgenda, Yii::$app->request->post())) {
$valid = $model->validate();
$valid = $modelAgenda->validade() && $valid;
if ($valid) {
$model->save(false);
$modelAgenda->save(false);
return $this->redirect(['view', 'id' => $model->id]);
}
}
return $this->render('update', [
'model' => $model,
'modelAgenda' => $modelAgenda
]);
}
My form view
<?= $form->field($modelAgenda, 'agenda_id')->checkboxList(Agenda::combo(), ['class' => 'checkbox']) ?>
<?= $form->field($model, 'bioma_id')->dropDownList(Bioma::combo(), ['prompt' => $prompt]) ?>
<?= $form->field($model, 'nome')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'tipo_doc')->radioList(['CPF'=>'CPF', 'CNPJ'=>'CNPJ'], ['class' => 'radio']) ?>
<?= $form->field($model, 'n_doc')->widget(MaskedInput::className(), ['mask' => ['999.999.999-99', '99.999.999/9999-99']]) ?>
<?= $form->field($model, 'observacao')->textarea(['rows' => 7]) ?>
What could be wrong?
EDIT (full error):
1) If you mean working with multiple models of the same type, the error is in this line:
$valid = $modelAgenda->validade() && $valid;
First, it should be $modelAgenda->validate(), second $modelAgenda contains array of models, validate() method can be called only on single model.
For validating multiple models Yii2 suggests using built-in method validateMultiple():
use yii\base\Model;
...
$valid = Model::validateMultiple($modelAgenda) && $valid;
Working with multiple models is well covered in official docs (Collecting Tabular Input).
Note that they recommend to index models array by id before like this:
$models = YourModel::find()->index('id')->all();
2) If you need just two models of different type, don't use findAll() because it's for finding multiple models and always returns array (even on empty result). Use new for create action and findOne() for update action to initialize models. Let's say you initialized two models, $firstModel and $secondModel, then you can load and save them like this:
$isSuccess = false;
Yii::$app->db->transaction(function () use ($isSuccess) {
$areLoaded = $firstModel->load(Yii::$app->request->post()) && $secondModel->load(Yii::$app->request->post();
$areSaved = $firstModel->save() && $secondModel->save();
$isSuccess = $areLoaded && $areSaved;
});
if ($isSuccess) {
return $this->redirect(['view', 'id' => $model->id]);
}
Transaction is added in case of saving of second model will fail (so the first model also will not be saved).
Alternatively, you can declare transactions inside your model, for example:
return [
'admin' => self::OP_INSERT,
'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,
// the above is equivalent to the following:
// 'api' => self::OP_ALL,
];
Then just use:
$firstModel->scenario = 'scenarioForTransaction';
$secondModel->scenario = 'scenarioForTransaction';
$areLoaded = $firstModel->load(Yii::$app->request->post()) && $secondModel->load(Yii::$app->request->post();
$areSaved = $firstModel->save() && $secondModel->save();
if ($areLoaded && $areSaved) {
return $this->redirect(['view', 'id' => $model->id]);
}
For more than two models, it's better to use loops.
P.S. I'd recommend to separate saving to different controllers / actions and call it via AJAX, it will be more user friendly.
For saving relations read - Saving Relations.
public function actionUpdate($id)
{
$model = $this->findModel($id);
$modelAgenda = AgendaFiscalizacao::findAll(['fiscalizacao_id' => $id]);
if ($model->load(Yii::$app->request->post()) && $modelAgenda->load(Yii::$app->request->post()) && Model::validateMultiple([$model, $modelAgenda])) {
$model->save(false);
$modelAgenda->save(false);
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('update', [
'model' => $model,
'modelAgenda' => $modelAgenda
]);
}
You can refer following link for example : http://blog.dedikisme.com/blog/2014/10/13/yii2-building-a-single-form-with-multiple-models
You are not rendering $modelAgenda from view/update.php to view/_form.php file while using render in update file.

yii2 - Kartik file input - update

This is the situation: I'm new on Yii2 and wanted to use some file uploader widget within ActiveForm.. so far I've found this excelent one: \kartik\widget\FileInput
With this widget I can manage file upload and then, when enter in edit mode, show the previous uploaded image with the oportunite to replace it.
The problem is that if I press the "Update" button of the form without modifying the image yii says that the image "can't be empty" because I've set the 'required' rule in my model.
After an awful afternoon and a more productive night, I've encountered a solution that worked for me..
The main problem was that file input don't send its value (name of the file stored in database) when updating. It only sends the image info if browsed and selected through file input..
So, my workaround was creating another "virtual" field for managing file upload, named "upload_image". To achieve this I simple added a public property with this name to my model class: public $upload_image;
I also add the folowing validation to rules method on Model class:
public function rules()
{
return [
[['upload_image'], 'file', 'extensions' => 'png, jpg', 'skipOnEmpty' => true],
[['image'], 'required'],
];
}
Here, 'upload_image' is my virtual column. I added 'file' validation with 'skipOnEmpty' = true, and 'image' is the field on my database, that must be required in my case.
Then, in my view I configured 'upload_image' widget like follows:
echo FileInput::widget([
'model' => $model,
'attribute' => 'upload_image',
'pluginOptions' => [
'initialPreview'=>[
Html::img("/uploads/" . $model->image)
],
'overwriteInitial'=>true
]
]);
In 'initialPreview' option I asign my image name, stored in '$model->image' property returned from database.
Finally, my controller looks like follow:
public function actionUpdate($id)
{
$model = $this->findModel($id);
$model->load(Yii::$app->request->post());
if(Yii::$app->request->isPost){
//Try to get file info
$upload_image = \yii\web\UploadedFile::getInstance($model, 'upload_image');
//If received, then I get the file name and asign it to $model->image in order to store it in db
if(!empty($upload_image)){
$image_name = $upload_image->name;
$model->image = $image_name;
}
//I proceed to validate model. Notice that will validate that 'image' is required and also 'image_upload' as file, but this last is optional
if ($model->validate() && $model->save()) {
//If all went OK, then I proceed to save the image in filesystem
if(!empty($upload_image)){
$upload_image->saveAs('uploads/' . $image_name);
}
return $this->redirect(['view', 'id' => $model->id]);
}
}
return $this->render('update', [
'model' => $model,
]);
}
I have encountered another solution by creating scenarios. In your case I would modify the rules like this:
public funtion rules() {
[['image'], 'file'],
[['image'], 'required', 'on'=> 'create']
}
So the fileupload field will be required only in create action. In update action I have this code:
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
$newCover = UploadedFile::getInstance($model, 'image');
if (!empty($newCover)) {
$newCoverName = Yii::$app->security->generateRandomString();
unlink($model->cover);
$model->cover = 'uploads/covers/' . $newCoverName . '.' . $newCover->extension;
$newCover->saveAs('uploads/covers/' . $newCoverName . '.' . $newCover->extension);
}
if ($model->validate() && $model->save()) {
return $this->redirect(['view', 'id' => $model->post_id]);
} else {
// error saving model
}
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
In the update scenario the image filed is not required but the code checks if nothing was uploaded and doesn't change the previous value.
My form file:
<?= $form->field($model, 'image')->widget(FileInput::classname(), [
'options' => ['accept'=>'image/*'],
'pluginOptions'=>[
'allowedFileExtensions'=>['jpg', 'gif', 'png', 'bmp'],
'showUpload' => true,
'initialPreview' => [
$model->cover ? Html::img($model->cover) : null, // checks the models to display the preview
],
'overwriteInitial' => false,
],
]); ?>
I think is a little more easier than a virtual field. Hope it helps!
Try preloading the file input field with the contents of that field. This way, you will not lose data after submitting your form.
I looked through kartik file-input widget (nice find, btw) and I came across a way to do this
// Display an initial preview of files with caption
// (useful in UPDATE scenarios). Set overwrite `initialPreview`
// to `false` to append uploaded images to the initial preview.
echo FileInput::widget([
'name' => 'attachment_49[]',
'options' => [
'multiple' => true
],
'pluginOptions' => [
'initialPreview' => [
Html::img("/images/moon.jpg", ['class'=>'file-preview-image', 'alt'=>'The Moon', 'title'=>'The Moon']),
Html::img("/images/earth.jpg", ['class'=>'file-preview-image', 'alt'=>'The Earth', 'title'=>'The Earth']),
],
'initialCaption'=>"The Moon and the Earth",
'overwriteInitial'=>false
]
]);
You may also want to relax the required rule in your model for that field, so it does not complain on validation. You may choose to prompt the user through subtler means.
From Krajee:
http://webtips.krajee.com/advanced-upload-using-yii2-fileinput-widget
Create, delete, update: really easy, look no further.
(1) I've set the 'required' rule in my model too.
(2) To work on Wampserver:
Yii::$app->params['uploadPath'] = Yii::$app->basePath . '/web/uploads/';
Yii::$app->params['uploadUrl'] = Yii::$app->urlManager->baseUrl . '/uploads/';

I cannot grab a $_POST value in controller using Yii 2.0

Using Yii 2.0 i'm trying to grab some $_POST values in my controller from my view but cannot do this. I will show you my code and talk you through it below.
Models/CaseSearch.php
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\Cases;
/**
* CaseSearch represents the model behind the search form about `app\models\Cases`.
*/
class CaseSearch extends Cases
{
public $category;
public $subcategory;
public $childcategory;
public $newcategory;
/**
* #inheritdoc
*/
public function rules()
{
return [
[['case_id', 'year'], 'integer'],
[['name', 'judgement_date', 'neutral_citation', 'all_ER', 'building_law_R', 'const_law_R', 'const_law_J', 'CILL', 'adj_LR'], 'safe'],
];
}
/**
* #inheritdoc
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$query = Cases::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'case_id' => $this->case_id,
'judgement_date' => $this->judgement_date,
'year' => $this->year,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'neutral_citation', $this->neutral_citation])
->andFilterWhere(['like', 'all_ER', $this->all_ER])
->andFilterWhere(['like', 'building_law_R', $this->building_law_R])
->andFilterWhere(['like', 'const_law_R', $this->const_law_R])
->andFilterWhere(['like', 'const_law_J', $this->const_law_J])
->andFilterWhere(['like', 'CILL', $this->CILL])
->andFilterWhere(['like', 'adj_LR', $this->adj_LR]);
return $dataProvider;
}
public function searchByCategory($category){
$query = Cases::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!$this->validate()) {
// uncomment the following line if you do not want to any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'category_id' => $category
]);
return $dataProvider;
}
}
Okay so now is my view:
<?php
$form = ActiveForm::begin();
echo $form->field($searchModel, 'category')
->dropDownList(
ArrayHelper::map($allCategory, 'id', 'name'),
[
'onchange'=>'getSubcategory()',
]
);
//To stop errors, if first category not chosen make subcategory and empty drop down.
$subcategory = array(
"empty" => ""
);
echo $form->field($searchModel, 'subcategory')
->dropDownList(
ArrayHelper::map($subcategory, 'id', 'name'),
[
'onchange'=>'getChildcategory()',
]
);
//To stop errors, if second category not chosen make childcategory and empty drop down.
$childcategory = array(
"empty" => ""
);
echo $form->field($searchModel, 'childcategory')
->dropDownList(
ArrayHelper::map($childcategory, 'id', 'name'),
[
//'onchange'=>'getChildCategory()',
'onchange'=>'submitNow()',
]
);
echo '<div class="form-group">';
echo Html::submitButton('Submit', ['class' => 'btn btn-primary']);
echo '</div>';
ActiveForm::end();
Ok, so when i click the submit button i want to capture the value in my controller so that i can use this to alter the results given in the gridview.
When i inspect the element on the drop down lists the names are weird so i am not sure if this is making a different. for example the name for subcategory is actually: CaseSearch[subcategory]
Now for my controller:
public function actionIndex()
{
//
// This is for the first render of index
//
$model = new Cases;
$searchModel = new CaseSearch();
$allCategory = Category::find()->all();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
//
// This is for when i click the submit button on the view. I want it to submit and then grab the subcategory in variable $subtest. I make $subtest = 1 so that when i first render the page it doesn't throw an error and when submitted it should change to the post value
//
$subtest = 1;
if($searchModel->load(Yii::$app->request->post())){
$subtest = $_POST['subcategory'];
}
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'allCategory' => $allCategory,
'model' => $model,
'subtest' => $subtest
]);
}
However when i try to print_r() the variable $subtest in my view i get the error:
Undefined index: CaseSearch[subcategory]
and its for the line:
$subtest = $_POST['CaseSearch[subcategory]'];
In my controller.
Can anyone please advise as i cannot figure out why?
I think your application design is improvable. Read the Yii guide.
However I think the solution to the question is:
$subtest = $_POST['CaseSearch']['subcategory];
Don't use $_GETand $_POST directly in your controller there are some abstraction layer which filter that input.
$get = $request->get();
// equivalent to: $get = $_GET;
$post = $request->post();
// equivalent to: $post = $_POST;
You should use that classes and methods to get your params.
http://www.yiiframework.com/doc-2.0/guide-runtime-requests.html

Categories