Yii2 update related model makes insert - activerecord

Depending on this question: Yii2 updating two related models does not show data of the second. I have manged calling the related model InvoiceItems to the Invoices model it hasMany relation.
However, updating leads to insert new records in invoice_items table instead of updating the current related records to the invoices table.
I tried to add the id field of each InvoiceItems record in the _form view to solve this issue, but it still exist.
The following is actionUpdate of the InvoicesController:
public function actionUpdate($id)
{
$model = $this->findModel($id);
//$invoiceItems = new InvoiceItems();
$count = count(Yii::$app->request->post('InvoiceItems', []));
//Send at least one model to the form
$invoiceItems = [new InvoiceItems()];
//Create an array of the products submitted
for($i = 1; $i < $count; $i++) {
$invoiceItems[] = new InvoiceItems();
}
if ($model->load(Yii::$app->request->post()) && $model->save()) {
//$invoiceItems->invoice_id = $model->id;
if (Model::loadMultiple($invoiceItems, Yii::$app->request->post())){
foreach ($invoiceItems as $item){
$item->invoice_id = $model->id;
//$item->id = $model->invoiceItems->id;
$item->save(false);
}
return $this->redirect(['view', 'id' => $model->id]);
}
else{
return var_dump($invoiceItems);
}
} else {
//$invoiceItems->invoice_id = $model->id;
$invoiceItems = $this->findInvoiceItemsModel($model->id);
return $this->render('update', [
'model' => $model,
'invoiceItems' => $invoiceItems,
]);
}
}
This is the code of _form view of InvoicesController:
<div class="invoices-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'created')->textInput() ?>
<?= $form->field($model, 'type')->textInput(['maxlength' => true]) ?>
<hr />
<?php if (is_array($invoiceItems)): ?>
<?php foreach ($invoiceItems as $i => $item): ?>
<?= $form->field($item, "[$i]id")->textInput();?>
<?= $form->field($item, "[$i]item_id")->textInput();?>
<?= $form->field($item, "[$i]unit_id")->textInput();?>
<?= $form->field($item, "[$i]qty")->textInput();?>
<?php endforeach; ?>
<?php else: ?>
<?= $form->field($invoiceItems, "item_id")->textInput();?>
<?= $form->field($invoiceItems, "unit_id")->textInput();?>
<?= $form->field($invoiceItems, "qty")->textInput();?>
<?php endif; ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
The following screen shot, visually, demonstrates what I have got:

if you in update you don't need new record you should remove
this part
$count = count(Yii::$app->request->post('InvoiceItems', []));
//Send at least one model to the form
$invoiceItems = [new InvoiceItems()];
//Create an array of the products submitted
for($i = 1; $i < $count; $i++) {
$invoiceItems[] = new InvoiceItems();
}
The $invoiceItems you create in this way are obviuosly "new" and then are inserted ..for an update the model must to be not new..
You have already the related models to save they came from post.. and from load multiple
If you however need to manage new model (eg: because you added new record in wide form update operation) you can test
$yourModel->isNewRecord
and if this is new check then if the user have setted properly the related fields you can save it with
$yourModel->save();
otherwise you can simple discart.. (not saving it)

I am pretty sure that this code below of course fills a array named $invoiceItems with the submitted data.
if (Model::loadMultiple($invoiceItems, Yii::$app->request->post())){
foreach ($invoiceItems as $item){
$item->invoice_id = $model->id;
//$item->id = $model->invoiceItems->id;
$item->save(false);
}
}
But all $items have the scenario "insert" (could be that you allow the setting of the attribute 'ID' but normally this isn't allowed in the Gii generated code. and then you get "new items" whenever you save.
if you add a return var_dump($invoiceItems);after the loadMultiple you will see that only the safe attributes are filled with the submitted data.
You are also not validating them before saving, which is also kind of bad.
if (Model::loadMultiple($invoiceItems, Yii::$app->request->post()) && Model::validateMultiple($invoiceItems)) {
http://www.yiiframework.com/doc-2.0/guide-input-tabular-input.html

According to scaisEdge answer and this widget documentation. I could able to determine the problem solution.
Simply, in my code I neglected the relation between the two models, when I say:
$count = count(Yii::$app->request->post('InvoiceItems', []));
//Send at least one model to the form
$invoiceItems = [new InvoiceItems()];
The value of $invoiceItems should be obtained using the relation like the following:
$invoiceItems = $model->invoiceItems;
However, I still have another issue with adding new records to the related model InvoiceItems during the update.

Related

Yii2 dropDownList get selected value

I have dropDownList in my view ( data from db) :
use yii\widgets\ActiveForm;
\app\assets\HelloAsset::register($this);
$form = ActiveForm::begin();
$users=\app\models\User::find()->all();
$items=\yii\helpers\ArrayHelper::map($users,'id','username');
echo $form->field($model, 'username')->dropDownList($items);
echo \yii\bootstrap\Html::submitButton('Send');
ActiveForm::end();
I want to get data from 'username' in my controller:
$model=new Data();
if ($model->load(\Yii::$app->request->post())){
echo 'it is status:'.$model->username;
}else {
return $this->render('simple', ['model' => $model]);
}
But data is null.
How I must get data in right?
Please, help me solve this problem.

Dependanat Drop down is not working when it has only one value in yii framework

I am using dependant dropdownlist for get value for subject dropdown when select value from 'Grade' dropdown. It is working fine. But the problem is when there are only one value in grade dropdown the subject dropdown is not updated.
this is my code:-
Grade DropDown----------
($data is consist of grade)
<?php echo CHtml::dropDownList('myDropDown1','',$data,array(
'id'=>'gr',
'empty' => '(Select Grade)',
'style'=>'width:200px',
'ajax' =>
array(
'type'=>'POST', //request type
'url'=>CController::createUrl('sub'), //action to call
'update'=>'#cls', // which HTML element to update
)
)); ?>
Subject Dropdown (which depend on grade dropdown)------------
<?php echo CHtml::label('Subject',''); ?>
<?php echo CHtml::dropDownList('myDropDown3','',array(),array(
'id'=>'sub',
'prompt'=> 'Please select a class', 'style'=>'width:150px',
'style'=>'width:200px',
)); ?>
<?php //echo $form->error($model,'sub_id'); ?>
In controller-------------------
public function actionClass()
{
$grd = $_POST['myDropDown1'];
$c_id = TbClass::model()->findAll('id=:id',
array(':id'=>$grd,));
$data3 = CHtml::listData($c_id,'id','grade');
$grd2 = array_shift($data3);
$sub1 = TbClass::model()->findAll('grade=:grade',
array(
':grade'=>$grd2,
));
$data4 = CHtml::listData($sub1,'id','class');
foreach($data4 as $value=>$name)
{
echo CHtml::tag('option',
array('value'=>$value),CHtml::encode($name),true);
}
}
This code is working fine. Problem is when grade has only one value in the dropdown , cannot update the subject dropdown.
I think your problem is in array_shift,
have you checked the value of $grd2 ?

Dynamic form in Yii

I'm having a problem creating something for my application. What i already have is a CJuiTabs system with a form in each tab. The number and values/names of the tabs is defined by a number of tinyInt columns in an another table in my database. The value of the tab is submitted with the form as a hidden field.
So now if you go to a view with an id you have already submitted a record with, you will be able to update that record, but you are not able to create new records with the other tab-values.
This is my problem. How do i make the application decide wether to create a new record or update an existing one based on which tab you have selected? If you are updating a record i would like to have the values of the record shown, but if you are creating a new one you should only see blank fields (well, its a rating system, so i guess it is stars not fields).
I guess it chould be done by using AJAX but i'm very much unsure of how to do that.
Game/view:
<?php
$tab_list=Platform::getPlatforms();
$tabarray=array();
// Create Dynamic Tabs
foreach($tab_list as $key=>$value){
$tabarray["$value"]=array(
'id'=>$key,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$ranking, 'key'=>$key),TRUE)
);
}?>
<?php
$this->widget('zii.widgets.jui.CJuiTabs',array(
'tabs'=>$tabarray,
'options'=>array(
'collapsible'=>true,
),
'id'=>'categorytabs',
)); ?>
Models/Platform:
const PC = 1;
const Mac = 2;
const XBOX = 3;
const XBOX360 = 4;
const PS2 = 5;
const PS3 = 6;
const PSP = 7;
const PSVITA = 8;
const Wii = 9;
const WiiU = 10;
const NintendoDS = 11;
const NintendoDS3 = 12;
...
public function getPlatforms()
{
$id = Yii::app()->request->getQuery('id');
$platform = Platform::model()->findByPk($id);
$platforms = array();
if ($platform -> pc == 1)
{
$platforms[self::PC] = "PC";
}
if ($platform -> xbox == 1)
{
$platforms[self::XBOX] = 'XBOX';
}
if ($platform -> xbox360 == 1)
{
$platforms[self::XBOX360] = "XBOX 360";
}
if ($platform -> ps2 == 1)
{
$platforms[self::PS2] = "PS2";
}
if ($platform -> ps3 == 1)
{
$platforms[self::PS3] = 'PS3';
}
if ($platform -> psp == 1)
{
$platforms[self::PSP] = "PSP";
}
if ($platform -> psVita == 1)
{
$platforms[self::PSVITA] = 'PS VITA';
}
if ($platform -> wii == 1)
{
$platforms[self::Wii] = "Wii";
}
if ($platform -> wiiU == 1)
{
$platforms[self::WiiU] = "Wii U";
}
if ($platform -> nintendoDS == 1)
{
$platforms[self::NintendoDS] = 'Nintendo DS';
}
if ($platform -> nintendoDS3 == 1)
{
$platforms[self::NintendoDS3] = 'Nintendo DS3';
}
return $platforms;
}
Controllers/GameController:
public function actionView($id)
{
...
$ranking=$this->createRanking($model);
...
}
protected function createRanking($model)
{
$user_id=Yii::app()->user->getId();
$game_id=$model->id;
$rank=ranking::model()->find("create_user_id=$user_id and game_id=$game_id");
if($rank===null){
$ranking=new Ranking;
}
else{
$ranking=$rank;
}
if(isset($_POST['Ranking']))
{
$ranking->game_id=$model->id;
$ranking->attributes=$_POST['Ranking'];
$valid = $ranking->validate();
if ($valid)
{
$ranking->save(false);
$this->redirect(array('index'));
}
}
return $ranking;
}
Ranking/_form:
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'ranking-form',
'enableAjaxValidation'=>false,
)); ?>
<p class="note">Fields with <span class="required">*</span> are required.</p>
<?php echo $form->errorSummary($model); ?>
<?php echo $form->hiddenField($model, 'platform_id', array('value' => $key)); ?>
<div class="row">
<?php echo $form->labelEx($model,'overall'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'overall',
)); ?>
<?php echo $form->error($model,'overall'); ?>
</div>
<br/>
<div class="row">
<?php echo $form->labelEx($model,'graphics'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'graphics',
)); ?>
<?php echo $form->error($model,'graphics'); ?>
</div>
<br/>
<div class="row">
<?php echo $form->labelEx($model,'sound'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'sound',
)); ?>
<?php echo $form->error($model,'sound'); ?>
</div>
<br/>
<div class="row">
<?php echo $form->labelEx($model,'gameplay'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'gameplay',
)); ?>
<?php echo $form->error($model,'gameplay'); ?>
</div>
<br/>
<div class="row">
<?php echo $form->labelEx($model,'lastingApp'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'lastingApp',
)); ?>
<?php echo $form->error($model,'gameplay'); ?>
</div>
<br/>
<div class="row buttons">
<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>
</div>
<?php $this->endWidget(); ?>
I'm not sure I get this completely, let me try to see if I get the problem,
You have multiple forms one user can edit, they're all forms to rank one game on different gaming platforms (XBOX, PS2/3, etc..).
If this is the case, in your Game controller:
$rank=ranking::model()->find("create_user_id=$user_id and game_id=$game_id");
will always return the same db entry regardless of gaming platform (provided there is an entry)
You would need to use something along the lines of
$rank=ranking::model()->find("create_user_id=$user_id and game_id=$game_id and platform_id=$platform_id");
That covers the saving part but there's also an issue in the displaying. You have:
foreach($tab_list as $key=>$value){
$tabarray["$value"]=array(
'id'=>$key,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$ranking, 'key'=>$key),TRUE)
);
In essence, you use the same $ranking model for each tab, so therefore the same (already existing) field inputs, or star ratings.
This is where it gets a little tricky. Ideally you would use Yii's relations To set up your models as Plateforms having multiple Games(MANY_MANY) that have multiple Ratings depending on Plateform/Game combination. Then you could do something along the lines of :
$platforms = Plateform::model()->findAll(array(
'with'=>array(
'rankings'=>array(
'condition'=>'game_id=$game_id AND platform_id=t.id')) //t refers to platform in Yii < 1.1.13. For Yii >= 1.1.13 check version update logs
));
foreach($platforms as $platform){
$tabarray[$platform->title]=array( //called it title arbitrarily, could be anything
'id'=>$platform->id,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$platform->rankings[0], 'key'=>$platform->id),TRUE)
);
(I'm sparing you the use of 'params'=>array(), but do make it a habit.)
However you don't seem like you've gone that route, so check it out if you want, if not how about changing createRanking() to return an array of rankings corresponding to a platform for a given game using a key=> value where the key is the platform id. Then you could do:
foreach($tab_list as $key=>$value){
$tabarray["$value"]=array(
'id'=>$key,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$rankings[$key], 'key'=>$key),TRUE)
);
If you went with that, as far as saving goes, if isset($_GET['Ranking']) then you would use the platform_id to isolate the correct model from the array in order to save it to db (or create a new one if it doesn't exist)
Hope that helps, or at least gets you on the right track.
edit: Added some code, not the best/cleanest solution but it should work and be easy to understand :
Controllers/GameController:
public function actionView($id)
{
...
$this->createRanking($model);
$rankings = $this->getRankingList();
...
}
//add this function
protected function getRankingList($gid,$uid)
{
$tab_list=Platform::getPlatforms();
$rankings = array();
foreach($tab_list as $key=>$value)
{
$rank=Ranking::model()->find("create_user_id=$user_id and game_id=$game_id and platform_id=$key");
if(empty($rank))
$rank = new Ranking;
$rankings[$key]= $rank;
}
return $rankings;
}
Game/view:
<?php
$tab_list=Platform::getPlatforms();
$tabarray=array();
// Create Dynamic Tabs
foreach($tab_list as $key=>$value){
$tabarray["$value"]=array(
'id'=>$key,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$rankings[$key], 'key'=>$key),TRUE)
);
}?>
<?php
$this->widget('zii.widgets.jui.CJuiTabs',array(
'tabs'=>$tabarray,
'options'=>array(
'collapsible'=>true,
),
'id'=>'categorytabs',
)); ?>

Codeigniter and Tank_auth sending validation to view not working

Hi I am new to Codeigniter but have hit a brick wall.
I am trying to see if a user already exists.
First I upload the data via a form to a controller which does its validation etc but breaks on only that issue. I managed to find where it breaks but cant fix it from there.
Prior to all this it querys the database finds there is in fact a match username and then reaches the snippet below
$errors = $this->tank_auth->get_error_message();
//find the correct error msg
foreach ($errors as $k => $v) $data['errors'][$k] =$this->lang->line($v);
//loop and find etc
$temp_mess = $data['errors'][$k];
//stores relevant stuff in the string
}
}
//echo $temp_mess; it outputs to the html so i can see it "says user exists"
$data['temp_mess'] = $tempmess; /// put this into a array
$this->load->view('layout', $data); ///send
}
}
Now for the view, it then calls the layout view etc but alas there is no output
$username1 = array(
'name' => 'username1',
'id' => 'username1',
'value' => set_value('username1'),
'maxlength' => $this->config->item('username_max_length', 'tank_auth'),
'size' => 30,
);
<?php echo form_open('register', $form_reg_id ); ?>
<fieldset>
<legend align="center">Sign up</legend>
<?php echo form_label('Username', $username1['id']); ?>
<?php echo form_input($username1); ?>
<?php $tempmess; ?>
<div class="error"><?php echo form_error($username1['name']); ?>
<?php echo isset($errors[$username1['name']])?$errors[$username1['name']]:''; ?>
<?php echo form_close(); ?>
</fieldset>
Thanks for any help on this
also could some one explain this line please. (for a really dumb person)
<?php echo isset($errors[$username1['name']])?$errors[$username1['name']]:''; ?>
Tank_auth automatically returns an error when the username exists. Juste have to check the errors array.

Issue with active record update in CodeIgniter

I have a form that submits data to a database in a CodeIgniter app (CI v. 2.0.2). We are allowing users to "edit" records by having them resubmit a record with new values and then doing an update. On submit, the form calls the vote controller's create method. Inside the create method, we check to see if there's already a record based on an entry code and the user's ID. If there is, we update; otherwise we create a new record. The creation works just fine; it's only the update I'm having an issue with. Here's the code.
view
<div id="vote_form">
<?php
$hidden = array('dot_judge_id' => $this->session->userdata('dot_judge_id'));
echo form_open('vote/create');
$entry_code_data = array(
'name' => 'entry_code',
'id' => 'entry_code',
'value' => set_value('entry_code')
);
echo form_hidden($hidden);
$score_options = array('1'=>'1 (lowest)', '2'=>'2','3'=>'3', '4'=>'4','5'=>'5 (highest)');
?>
<p><label for="entry_code">Entry code: </label><?php echo form_input($entry_code_data); ?></p>
<p><label for="q1">Question 1: </label><?php echo form_dropdown('q1', $score_options, ''); ?></p>
<p><label for="q2">Question 2: </label><?php echo form_dropdown('q2', $score_options, ''); ?></p>
<p><label for="q3">Question 3: </label><?php echo form_dropdown('q3', $score_options, ''); ?></p>
<p><label for="q4">Question 4: </label><?php echo form_dropdown('q4', $score_options, ''); ?></p>
<p><label for="q5">Question 5: </label><?php echo form_dropdown('q5', $score_options, ''); ?></p>
<p><?php echo form_submit('submit', 'Submit vote'); ?></p>
<?php echo form_close(); ?>
<?php echo validation_errors(); ?>
</div>
controller
function create() {
$id = $this->input->post('entry_code');
$judge_id = $this->input->post('dot_judge_id');
$data = array(
'entry_code' => $id,
'dot_judge_id' => $judge_id,
'q1' => $this->input->post('q1'),
'q2' => $this->input->post('q2'),
'q3' => $this->input->post('q3'),
'q4' => $this->input->post('q4'),
'q5' => $this->input->post('q5'),
);
//first check to see if there's already a record for this judge/entry
//if so, update. Otherwise, insert
$vote_id = $this->vote_model->getEntryById($id, $judge_id);
if($vote_id) {
log_message('debug', 'vote id exists: '.$vote_id);
$this->vote_model->updateRecord($data, $vote_id);
}
else {
log_message('debug', 'vote id does not exist; creating new');
$this->vote_model->createRecord($data);
}
/*
after submission, go to another page that gives choices - review entries, submit another entry, log out
*/
$data['msg'] = "Entry submitted";
$this->menu();
}
model
function getEntryByID($id, $judge_id) {
//determine if record already exists for entry/judge
$sql = 'SELECT vote_id from dot_vote WHERE entry_code = ? AND dot_judge_id = ?';
$query = $this->db->query($sql, array($id, $judge_id));
if($query->num_rows() == 1) {
$row = $query->row();
return $row->vote_id;
}
else {
return false;
}
}
function createRecord($data) {
$this->db->insert('dot_vote', $data);
return;
}
function updateRecord($data, $vote_id) {
log_message('debug', 'vote id is passed: '.$vote_id);
$this->db->where('vote_id', $vote_id);
$this->update('dot_vote', $data);
}
I know it's making it into the updateRecord method because the log_message output is in my log file displaying the correct vote_id (the auto-increment field of the returned record). But what I get in the browser is the following:
Can anyone point me in the right direction here? I have error display on in my config file, and have gotten SQL errors when they occur, so I don't think this is a SQL error. But I have no idea what could be happening in that tiny little function that's causing this. My next step is to skip the active record update and just use a standard query, but I'd like to know what's causing the problem here, even if I can get it to work the other way.
This line stood out:
$this->update('dot_vote', $data);
Do you mean this?
$this->db->update('dot_vote', $data);

Resources