Deram
Deram

Reputation: 5

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(); ?>

Upvotes: 0

Views: 2901

Answers (1)

Pomme.Verte
Pomme.Verte

Reputation: 1782

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',
)); ?>

Upvotes: 1

Related Questions