Yii2 multiple models in one form - php

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.

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

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 Insert multiple records of a same table

I Have my model with 2 fields Product.php:
[['ID_PRODUCT'], 'integer'],
[['NAME_PRODUCT'], 'string'],
my Controller ProductController.php:
public function actionCreate()
{
$model = new Product();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->ID_PRODUCT]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
And i want insert many times the same table with ActiveForm:
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'ID_PRODUCT')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'NAME_PRODUCT')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'ID_PRODUCT')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'NAME_PRODUCT')->textInput(['maxlength' => true]) ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
But when i save the information the fields are overwritten and only the last record is inserted
What you are trying to do is collect, validate and save tabular data. The reason it doesn't work is that in the form, Yii generates a name tag based on the field name and model, e.g. name="[Product]["ID_PRODUCT"]. When the form is sent to the server, the first fields get overwritten by the last ones, as they have the same name. The correct way to collect tabular input in a form is to add brackets at the end of the name, like this; name="[1][Product]["ID_PRODUCT"].Using this method, Yii gives ways of loading and validating multiple models.
Modify your controller code to use multiple models;
<?php
namespace app\controllers;
use Yii;
use yii\base\Model;
use yii\web\Controller;
use app\models\Product;
class ProductController extends Controller
{
public function actionCreate(){
//Find out how many products have been submitted by the form
$count = count(Yii::$app->request->post('Product', []));
//Send at least one model to the form
$products = [new Product()];
//Create an array of the products submitted
for($i = 1; $i < $count; $i++) {
$products[] = new Product();
}
//Load and validate the multiple models
if (Model::loadMultiple($products, Yii::$app->request->post()) && Model::validateMultiple($products)) {
foreach ($products as $product) {
//Try to save the models. Validation is not needed as it's already been done.
$product->save(false);
}
return $this->redirect('view');
}
return $this->render('create', ['products' => $products]);
}
}
Now you have all the data you need to populate the form, including any error messages generated for individual instances of you product model. The view file for the form needs to be altered like this, to use the multiple models;
foreach ($products as $index => $product) {
echo $form->field($product, "[$index]ID_PRODUCT")->label($product->ID_PRODUCT);
echo $form->field($product, "[$index]NAME_PRODUCT")->label($product->NAME_PRODUCT);
}
All of this is covered in the Yii2 documentation

model->attributes in Yii2 always has NULL value

I have one temporary model as viewModel. In my CRUD actions (for example actionCreate) I want to get this viewModel data and assign that to a ActiveRecord model. I used below code but my model object atrribute always show NULL value for attributes:
$model = new _Users();
if ($model->load(Yii::$app->request->post())) {
Yii::info($model->attributes,'test'); // NULL
$attributesValue =[
'title' => $_POST['_Users']['title'],
'type' => $_POST['_Users']['type'],
];
$model->attributes = $attributesValue;
Yii::info($model->attributes,'test'); // NULL
$dbModel = new Users();
$dbModel->title = $model->title;
$dbModel->type = $model->type . ' CYC'; // CYC is static type code
Yii::info($dbModel->attributes,'test'); // NULL
if ($dbModel->save()) {
return $this->redirect(['view', 'id' => $dbModel->id]); // Page redirect to blank page
}
}
else {
return $this->render('create', [
'model' => $model,
]);
}
I think $model->load(Yii::$app->request->post()) not working and object attribute being NULL. Is it Yii2 bug or my code is incorrect??
If there is no rule for your attribute the $model->load() will ignore those not in the rules of the model.
Add your attributes to the rules function
public function rules()
{
return [
...
[['attribute_name'], 'type'],
...
];
}
To fetch data for an individually attributes(db-fields) in yii2.0 then you should just do as:
echo $yourModel->getAttribute('email');
ActiveRecord $attributes is a private property
Use $model->getAttribute(string)
You can use following codes:
$model = new _Users();
$model->attributes=Yii::$app->request->post('_Users');
$model->title= $model->title
$model->type = $model->type . ' CYC'; // CYC is static type code
#$model->sampleAttribute='Hello World';
Declare attribute as private then
echo $yourModel->attribute
work as expected
You must remove all public properties (title, type, etc.) in your _User model and $model->attributes = $post will work correctly.
I have also encountered the same problem, i Add my attributes to the rules function,but also error. And i found the reason for this problem. It is beause that the submit form's name in corresponding view file is not the same as the model's name which you use in controller
[controller file]:
$model=new SearchForm();
[view file]:
<input name="SearchForm[attribus]" ...
or
[view file]:
<?= $form->field($model,'atrribus')->textInput()?>

Categories