Foreach save

SmauG

Member
Hello,
I'm having troubles doing a dinamyc list with a combobox on each row allowing users to select an option and saving them all with a single save button.
I'll post as i have it right now, probably bad after a lot of tries and editions. It's still throwing this error:
  • The existing data required by the data writer could not be found.
  • The field 'training_group' was not recognised.
Controller
Code:
    public function actionTraining ()
    {
        //Selecciona la ID del usuario   
        $_userId = $this->_input->filterSingle('user_id', XenForo_Input::UINT);
 
        if ($_userId){
            $userId = $this->_input->filterSingle('user_id', XenForo_Input::UINT);
        }else{
            $userId =  XenForo_Visitor::getUserId();
        }   
        $teamId = ($this->getModelFromCache('XenForo_Model_Teams')->getTeamId($userId));
        $team = ($this->getModelFromCache('XenForo_Model_Teams')->getTeamByUser($userId));
        $players = ($this->getModelFromCache('XenForo_Model_Teams')->getPlayers($teamId));
 
       
        $viewParams = array(
            'players' => $players,
            'team' => $team
        );
 
        return $this->responseView('XenForo_ViewPublic_Misc_Empty', 'team_training', $viewParams);
    }
   
    public function actionTrainingPlayerUpdate ()
    {
        $visitor = XenForo_Visitor::getInstance();
        if (!$visitor->get('user_id')) {
            return $this->responseError(new XenForo_Phrase('cannot_edit_training'));
        }
        $userId = $this->_input->filterSingle('user_id', XenForo_Input::UINT);   
 
        $settings = $this->_input->filter(array(
            'player_id' => array(XenForo_Input::UINT, 'array' => true),
            'training_group' => XenForo_Input::STRING)
        );
        $writer = $this->_savePlayer($settings, $errors);
 
 
        return $this->responseRedirect(
            XenForo_ControllerResponse_Redirect::SUCCESS,
            XenForo_Link::buildPublicLink('team/training'),
            null);
    }
   
    protected function _savePlayer($settings, &$errors)
    {
        $writer = XenForo_DataWriter::create('XenForo_DataWriter_Player');
        $writer->setExistingData($settings['player_id']);
        $writer->set('training_group', $settings['training_group']);
 
        $writer->preSave();
 
        /*if ($dwErrors = $writer->getErrors())
        {
            $errors = (is_array($errors) ? $dwErrors + $errors : $dwErrors);
            return false;
        }*/
 
        $writer->save();
        return $writer;
    }

Datawriter
Code:
class XenForo_DataWriter_Player extends XenForo_DataWriter
{   
    protected function _getFields()       
    {
        array(   
        'xf_player' => array(
            'player_id'    => array( 'type' => self::TYPE_UINT, 'autoIncrement' => true ),
            'training_group'                => array('type' => self::TYPE_UINT,    'required' => false, 'default' => ''),
        )
       
        );
    }
   
    /**
    * Gets the actual existing data out of data that was passed in. See parent for explanation.
    *
    * @param mixed
    *
    * @see XenForo_DataWriter::_getExistingData()
    *
    * @return array|false
    */
    protected function _getExistingData($data)
    {
        if (!$teamId = $this->_getExistingPrimaryKey($data, 'player_id'))
        {
            return false;
        }
 
        return array('xf_player' => $this->_getPlayerModel()->getPlayer($playerId));
       
    }
   
    protected function _getUpdateCondition($tableName)
    {
        return 'player_id = ' . $this->_db->quote($this->getExisting('player_id'));
    }
   
    /**
    * @return XenForo_Model_Players
    */
    protected function _getPlayerModel()
    {
        return $this->getModelFromCache('XenForo_Model_Player');
    }   
}

Template
Code:
<xen:foreach loop="$players" value="$player" i="$i">
<form action="{xen:link team/trainingPlayerUpdate}" method="post" class="Form">
        <label for="ctrl_custom_title">{$player.name} {$player.surname}:</label>
                <select name="training_group[]" class="textCtrl OptOut" id="ctrl_style_id" autofocus="true">
                            <option value="'1'">1</option>
                            <option value="'2'">2</option>
                    </optgroup>
                </select>
    {$player.training_group}
    <input type="hidden" name="player_id" value="{$player.player_id}" /><br />
 
</xen:foreach>
    <input type="submit" value="{xen:phrase save}" class="button primary" accesskey="s" />
    <input type="hidden" name="_xfToken" value="{$visitor.csrf_token_page}" />
</form>

Hope someone could help here :)

PS: All query are requesting all fields (select *) so no info missed there.

Regards
 
Anyone able to help here? I'm pretty sure that it's a stupid problem, but i can't find it so i'm stuck here.
 
There may be other issues, but at first glance in your _getExistingData method you named your variable $teamId, but you're feeding in $playerId instead, which makes sense from reading the errors since it's not able to find the existing data.
 
Solved that, thanks. But i'm still with the same error and i'm seeing there are probably an error on the catching and processing the data from the template, but i'm not sure at all about how to finally do it.
 
<xen:foreach loop="$players" value="$player" i="$i">
<form action="{xen:link team/trainingPlayerUpdate}" method="post" class="Form">
<label for="ctrl_custom_title">{$player.name} {$player.surname}:</label>
<select name="training_group[]" class="textCtrl OptOut" id="ctrl_style_id" autofocus="true">
<option value="'1'">1</option>
<option value="'2'">2</option>
</optgroup>
</select>
{$player.training_group}
<input type="hidden" name="player_id" value="{$player.player_id}" /><br />

</xen:foreach>
<input type="submit" value="{xen:phrase save}" class="button primary" accesskey="s" />
<input type="hidden" name="_xfToken" value="{$visitor.csrf_token_page}" />
</form>

The first thing that I noticed is highlighted in red. You are creating a new form with each player, but closing only one form. The <xen:foreach...> should be below the <form action...>
 
I'll need a hand here.. I guess i've probably located where is the error, but i can't find it. The error is probably located on template (how variables are sent to controller update) or on controller update (how variables are read there), because i've tried many different ways of saving and even read but the error is the same, ever located on how i read (so it's from template or on read, i guess dw is ok).
 
Argh. Found a BIG error, i forgot to add the return on the _getFields function on datawriter so it wasn't working..

Now is working with last case so i have an error with the arrays. Could someone take a look?
 
Code:
$settings = $this->_input->filter(array(
            'player_id' => array(XenForo_Input::UINT, 'array' => true),
            'training_group' => XenForo_Input::STRING)
        );
You have player_id set as Unsigned Interger, but you set 'array' as true. Also in your DW you have player_id set to UINT and auto-increment:
Code:
'player_id'    => array( 'type' => self::TYPE_UINT, 'autoIncrement' => true )

Is the player_id suppose to be equal to the user_id? If so all you need to do is validate if that user_id is in the table, and if it is not, to insert it.
 
player_id it's a different id than user_id, it identifys a row on the new table. On my full table it's added, i've just reduced it here due comodity.

I pick all the player info (select * wich contains player_id, training_group and user_id) and i just wanna update the players own by user_id (done) letting change the training_group of each one saving them all with a single save button (problem here with the array, it just works for last player).
Actually i'm picking all players from an user, doing a foreach with a single form and showing them all with a combobox of training_group for each one. I guess i'm doing bad the send of receive of data of each player. I mean, i send 2 int of each player (player_id and training_group) and i wish to save them (all players) on an array to do a bulkset updating the training_group of each one. Or if there's a better way to do this, do it.

Sorry if i messed it up even more, hope you can help.
 
SmauG, zip up what you have and send a copy to steve[at]mythotical[dot]com and I will take a close look at it.
 
Mythotical, email sent :)

To everyone else, i made it work with a button on each row. But how can i make it work with a single button for the whole foreach (putting the save button outside the foreach instead inside)?
 
Meh, a totally stupidity, done.

It's just needed to send an array from the template ([] at the end of the variable), read it on the controller as an ARRAY_SIMPLE and do a loop, creating a dw instance, modifying the row and saving it all inside the loop. Pretty easy but made me crazy for few days with different trys.

Thank you all :)
 
Top Bottom