Question

Decently new to CakePHP and trying to work through a complicated (at least in my eyes, welcome simplification) association and having some difficulty. Bear with me here as it seems like a lot of info.

Pretty much, I have a dynamic list of parameters and associated dynamic values, so I build a model-less form in my view by getting all the hasMany ParameterValues of the parameter.

Controller:

$this->loadModel('Parameter');
$this->set('parameters', $this->Parameter->find('all'));

View:

            echo $this->Form->create(false, array(
                                        'inputDefaults' => array(
                                            'div' => 'form-group',
                                            'wrapInput' => false,
                                            'class' => 'form-control'
                                        ),
                                        'class' => 'well form-horizontal',
                                        'url' => '/dashboard'
                                    ));

            foreach($parameters as $parameter) 
            {
                $options = array();
                $selected = array();
                foreach($parameter['ParameterValue'] as $parameter_value)
                {
                    if(strtolower($parameter_value['default']) == false || $parameter['Parameter']['is_multivalue'])
                    {
                        $options[$parameter_value['id']] = $parameter_value['value'];
                    }
                }

                echo $this->Form->input($parameter['Parameter']['name'], array(
                    'label' => __($parameter['Parameter']['name']),
                    'type'=>'select',
                    'options'=>$options,
                    'required'=>true,
                    'multiple'=>$parameter['Parameter']['is_multivalue'],
                    'error' => array('isValid' => 'Must enter a birthday.'),
                    'after' => ($parameter['Parameter']['is_multivalue'] ? '<span class="help-block">Select one or more options.</span>' : ''),
                    'empty'=> ($parameter['Parameter']['is_multivalue'] ? '' : 'Pick An Option')));
            }

            echo $this->Form->submit('Submit', array(
                                            'div' => 'form-group',
                                            'class' => 'btn btn-default'
                                        ));
            echo $this->Form->end();

This works fine, and I can set up the connections in the backend manually and see the selected values show up here. The issue I am having is taking the full set of values selected and then saving them into the database.

User Model:

App::uses('User', 'Users.Model');
class CustomUser extends User 
{
    public $name = 'CustomUser';

    public $useTable = 'users';

    public $hasMany = array(
            'ParameterValueUser'
        );  
}

ParameterValueUser Model:

App::uses('AppModel', 'Model');
class ParameterValueUser extends AppModel 
{
    public $name = 'ParameterValueUser';

    public $useTable = 'parameter_values_users';

    public $belongsTo = array(
            'CustomUser'=>array(
                'className'    => 'CustomUser',
                'foreignKey'   => 'user_id'
            ),
           'ParameterValue' => array(
                'className'    => 'ParameterValue',
                'foreignKey'   => 'parameter_value_id'
            )
        );
}

ParameterValue Model:

class ParameterValue extends AppModel 
{
    public $name = 'ParameterValue';

    public $actsAs = array('Containable');

    public $belongsTo = array(
        'Parameter'=>array(
            'className' => 'Parameter',
            'foreignKey' => 'parameter_id')
        );

    public $hasMany = array('ParameterValueUser');
}

Then I am saving the data by rebuilding the needed array like so:

CustomUserController action dashboard:

    if (!empty($this->request->data)) 
    {
        $data = array();

        $data['CustomUser']['id'] = $this->Auth->user('id');

        foreach($this->request->data as $key => $values)
        {
            if(is_array($values))
            {
                foreach($values as $value)
                {
                    $data['ParameterValueUsers'][] = array('parameter_value_id'=>$value);
                }
            } else {
                $data['ParameterValueUsers'][] = array('parameter_value_id'=>$values);
            }
        }

        $this->CustomUser->saveAll($data, array('deep' => true));
        $this->set('result', $data);
    }

Which gives me an output like so for 8 parameters

Array
(
[CustomUser] => Array
    (
        [id] => 52f44de4-ccb0-4a87-9861-1830c6fc40a8
    )

[ParameterValueUsers] => Array
    (
        [0] => Array
            (
                [parameter_value_id] => 1
            )

        [1] => Array
            (
                [parameter_value_id] => 5
            )

        ...

        [7] => Array
            (
                [parameter_value_id] => 209
            )

    )

)

The goal is to overwrite all links that are there with the form values linking each user to a parameter. I tried HABTM but it wasn't returning any data, this on the other hand, does not save anything to my join table. The user and parameter value are always guaranteed to exist.

Any input would be appreciated, glad to answer more questions.

Was it helpful?

Solution

After playing for a while I figured out the error of my ways was pretty much in what I was passing into saveAll. A lot of examples showed a keyed array with indexed sub array for saving many relationships, which led me astray (or I used it wrong)

The final code looks something like this:

    if (!empty($this->request->data)) 
    {
        $data = array();

        foreach($this->request->data as $key => $values)
        {
            if(is_array($values))
            {
                foreach($values as $value)
                {
                    $data['ParameterValueUsers'][] = array('parameter_value_id'=>$value,'user_id'=>$this->Auth->user('id'));
                }
            } else {
                $data['ParameterValueUsers'][] = array('parameter_value_id'=>$values,'user_id'=>$this->Auth->user('id'));
            }
        }
        $this->CustomUser->ParameterValueUser->deleteAll(array('user_id' => $this->Auth->user('id')));
        $this->loadModel('ParameterValueUser');
        $this->ParameterValueUser->saveAll($data['ParameterValueUsers']);
    }

Where I enforce HABTM like behavior by deleting all the old associations first. The trick was passing in the indexed portion of the array, not the full keyed parent array. The saving had to be done directly on the join model in order to get it to work (though I will take any input on how to improve the way I did this).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top