Yii2 save array of form fields to a single database field - php

How can I save an array of fields in Yii2 the current/default setup only works for field which aren't array.
Below are the form fields I need to save into the single field:
<div class="repeat">
<table class="wrapper" width="100%">
<thead>
<tr>
<td width="10%" colspan="4"><span class="add">Add</span></td>
</tr>
</thead>
<tbody class="container">
<tr class="template row">
<td width="10%"><span class="move">Move</span></td>
<td width="10%">An Input Field</td>
<td width="70%">
<?= $form->field($model, 'field1ofarray[{{row-count-placeholder}}]')->textInput(['maxlength' => 255])->label('Field Label') ?>
<?= $form->field($model, 'fieldofarray[{{row-count-placeholder}}]')->textInput(['maxlength' => 255])->label('Som field') ?>
<?= $form->field($model, 'field3ofarray[{{row-count-placeholder}}]')->textInput(['maxlength' => 255])->label('Field Label') ?>
<?= $form->field($model, 'field4ofarray[{{row-count-placeholder}}]')->dropDownList(['instock' => 'Instock', 'soldout' => 'Sold Out', 'scheduled' => 'Scheduled']); ?>
</td>
<td width="10%"><span class="remove">Remove</span></td>
</tr>
</tbody>
</table>
Current Controller (I need to know how I can loop through array and save as well as saving other normal fields in my form):
public function actionCreate()
{
$model = new GrailWall();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}

In my case I didn't need to make any changes to the controller at all.
You can just make a field in your db record, something like 'config_json` and then define a virtual property with getter and setter in your model.
public function getConfig()
{
return json_decode($this->config_json);
}
public function setConfig($value)
{
$this->config_json = json_encode($value);
}
Also set your virtual property to be safe in the rules so Massive Assignment works.
public function rules()
{
return [
[['company_id', 'created_at', 'updated_at'], 'integer'],
[['class'], 'required'],
[['config_json'], 'string'],
[['class'], 'string', 'max' => 255],
[['config'], 'safe']
];
}
Now you can set inputs like this in your view
<?= $form->field($model, 'config[ga_id]', ['labelOptions' => ['label' => 'Google Analytics Tracking ID']])->textInput(['maxlength' => true]) ?>

you can use loadMultiple(), look at this
this is about tabular/multiple input.
Otherwise load the modal you need using load($_POST[your_form_name].

models
public function getNetworkTypeArray()
{
return explode(',', $this->network_type);
}
public function setNetworkTypeArray(array $value)
{
$this->network_type = implode(',', $value);
}
controller
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
$model->setNetworkTypeArray($model->network_type);
if ($model->save()) {
return $this->redirect(['index']);
}
}
$model->network_type = $model->getNetworkTypeArray();
return $this->render('update', [
'model' => $model,
]);
}

$form->field($form, 'email[]')->textInput(['id' => 'email-0']);
will render:
<input type="text" name="Form[email][]" id="email-0">
Please note double square brackets in ($form, 'email[]').
ID attribute must be set explicitly unique, otherwise duplicate IDs will render, which does not conform with W3C/javascript standards.

instead saving in array you can save in string, to that use tne variables as normal in the view, ex:
fieldofarray instead fieldofarray[],
and in the controller use the implode function, ex:
public function actionCreate()
{
$model = new someModel();
if ($model->load(Yii::$app->request->post())) {
$model->fieldofarray = implode (",",$model->fieldofarray);
$model->save();
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}

Related

Value not updated in databases after validation and save success

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']],
];
}

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,
]);
}

Getting data for 2 different models - Yii2

I have a form that inside looks like that:
<?= $form->field($model, 'comment')->textarea(['rows' => 6]) ?>
<?= $form->field($presentation, 'attendance')->textInput(['maxlength' => true]) ?>
<?= $form->field($presentation, 'couples')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'hire_price')->textInput(['maxlength' => true]) ?>
Then I collect received data with controller, like that:
$model = new PresentationPlaceHistory();
$presentation = new Presentations();
if ($model->load(Yii::$app->request->post()) && $model->save() && $presentation->load(Yii::$app->request->post())) {
$pres = $presentation->findOne($model->presentation_id);
$pres->attendance = $presentation->load(Yii::$app->request->post('attendance'));
$pres->couples = $presentation->load(Yii::$app->request->post('couples'));
$pres->save();
return $this->redirect(['view', 'id' => $model->id]);
}
else {
return $this->render('create', [
'model' => $model,
'presentation' => $presentation,
]);
}
But in the effect, it saves all, beside 'attendance' and 'couples', which are set to 0 (before this form they were null).
It doesn't matter what number I put, I get zero. It doesn't even have to be number, because validation doesn't work at all.
Change following two lines:
$pres->attendance = $presentation->load(Yii::$app->request->post('attendance'));
$pres->couples = $presentation->load(Yii::$app->request->post('couples'));
// to this
$pres->attendance = $presentation->attendance;
$pres->couples = $presentation->couples;
You have already loaded $presentation values here $presentation->load(Yii::$app->request->post()) and should be available to access directly.
Kinda messy code u got here. First of all, I suggest you not to use load() method, but its my personal preference. Read more about load.
This method returns boolean, that is why you are getting 0 in model properties. In general, your code should look more like:
$model->load(Yii::$app->request->post());
if ($model->save()) {
$pres = $presentation->findOne($model->presentation_id);
$pres->attendance = $presentation->attendance;
//etc ...
$pres->save()
}
I dont know what is the point of your code, but this looks kinda pointless. Try to work with attributes, it is an array of all model properties. Or assign manually wanted properties of the model.
problem with your controller code, change following two lines
if ($model->load(Yii::$app->request->post()) && $model->save() && $presentation->load(Yii::$app->request->post())) {
$post_data= Yii::$app->request->post();
$pres = $presentation->findOne($model->presentation_id);
/* change these two lines */
$pres->attendance = $post_data['Presentations']['attendance'];
$pres->couples =$post_data['Presentations']['couples'];
/* change these two lines */
$pres->save();
return $this->redirect(['view', 'id' => $model->id]);
}
else
{
return $this->render('create', [
'model' => $model,
'presentation' => $presentation,
]);
}

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.

Categories