1
votes

We have two models which are related by a has and belongs to many (HABTM) relationship: Jobs, Tests. We are able to add/edit the relationships successfully (we know because they show up in the join table), but we can't get the existing values to appear in the view. The select/checkboxes (we've tried both) are always empty.

Here are the model relationships:

//Job.php
public $hasAndBelongsToMany = array (
    'Test' => array (
        'classname' => 'Test', 
        'foreignKey'=>'job_id',
        'joinTable' => 'jobs_tests',
        'associatedForeignKey' => 'test_id'
    )
);

//Test.php
    public $hasAndBelongsToMany = array(
        'Job' => array(
            'className'=> 'Job',
            'joinTable'=>'jobs_tests',
            'foreignKey' => 'test_id',
            'associatedForeignKey'=> 'job_id'
            )

    );

Here is the /view/Jobs/edit.ctp

            echo $this->Form->select('Test', $test_options, array('class'=>'form-control', 'multiple'=>'checkbox'));
//This is always empty (nothing selected/checked). 

What are we doing wrong?

Update:

Here is the JobsController action:

public function admin_edit( $id=NULL ) {
    $this->layout = 'admin';
    if (!$id)
        $this->redirect( array('controller'=>'jobs', 'action'=>'index'));

    $this->loadModel('Company');
    $companies = $this->Company->find('all');
    $company_options = array();
    foreach ($companies as $company) {
        $company_options[ $company['Company']['id'] ] = $company['Company']['name'];
    }
    $this->set('company_options', $company_options);

    $this->loadModel('Test');
    $tests = $this->Test->find('all');
    $tests_options = array();
    foreach ($tests as $test) {
        $test_options[ $test['Test']['id'] ] = $test['Test']['name'];
    }
    $this->set('test_options', $test_options);

    $category_options = $this->Job->validCategories;

    $this->set('category_options', $category_options);

    if ($this->request->isPut() ) {
        $data = $this->request->data;
        //debug($data);exit;
        $save = $this->Job->save( $data );
        if ($save) {
            $this->Session->setFlash('Job edited');
            //$job = $this->Job->findById( $id );
        } else {
            $this->Session->setFlash('Error editting job');
        }


    } 

    $job = $this->Job->findById($id);
    $this->request->data = $job;
    $this->set('job', $job);
}

Here is the form in the admin_edit.ctp view:

<?php echo $this->Form->create('Job'); ?>
    <fieldset>
        <?php
            echo $this->Form->input('id', array('type'=>'hidden'));
            echo $this->Form->input('name', array('class'=>'form-control')); 
            echo $this->Form->input('email', array('class'=>'form-control'));
            echo $this->Form->input('location', array('class'=>'form-control'));

            echo '<label>Type</label>';
            echo $this->Form->select('type', array('FT'=>'Full Time', 'PT'=>'Part Time', 'IN'=>'Internship'), array('empty'=>false, 'class'=>'form-control'));

            echo '<label>Company</label>';
            echo $this->Form->select('company_id', $company_options, array('class'=>'form-control')); 
            echo $this->Form->input('short_description', array('label' => 'Short Description', 'class'=>'form-control'));
            echo $this->Form->input('full_description', array('type'=>'textarea', 'label' => 'Full Description', 'class'=>'form-control'));
            echo $this->Form->input('is_private', array('label'=>'Is Private?', 'class'=>'form-control') );

            echo '<label>Category</label>';
            echo $this->Form->select('category', $category_options, array('class'=>'form-control'));
            echo '<label>Tests</label>';            
            //echo $this->Form->select('Test.id', $test_options, array('class'=>'form-control', 'multiple'=>true));

            $selected = array();
            foreach ($job['Test'] as $test) {
                $selected[]=$test['id'];
                //$selected[ $test['id'] ] = $test['name'];
            }
            debug($selected);
            echo $this->Form->input('Test', array('type'=>'select', 'options'=>$test_options, 'class'=>'form-control', 'multiple'=>'checkbox', 'selected'=>$selected));
        ?>
    </fieldset>
    <?php echo $this->Form->end(__('Submit')); ?>
2
On a side note, it's associationForeignKey, not associatedForeignKey, but it's not necessary to define these values when following the conventions, as CakePHP will then pick the correct names automatically. - ndm
Apart from the fact that you are making your life way more complicated then necessary, and that'll always lose your input in case the form cannot be saved as you are always overwriting it (Is that maybe your problem? Losing the selections after submitting the form?), I can't see why it shouldn't work, assuming $test_data and $selected do contain the expected data. That being said, it would be helpful to see what exactly they contain. - ndm
@ndm An example of $test_data: array( (int) 0 => '5', (int) 1 => '7' ) . The data is not lost after submission. The defaults/current selections all appear fine. Only the HABTM association is problematic. - emersonthis
Sorry, I ment $test_options, not $test_data. Also it looks like you are showing the contents of $selected, as $test_options should be in id => name format according to your controller code? - ndm
@ndm I was confused too. What I just sent was $test_options, but we have also tried with the key=>value version: array( (int) 5 => 'WSAT Practice', (int) 7 => 'WSAT-PE' ) and that also did not work. As for the matter of the data being reset... I don't think that is actually happening because I debug it in the view and all the data is there (including associations). Am I missing something? - emersonthis

2 Answers

2
votes

PHEW! This was a stumper but the solution turned out to be simple... The values in $options['selected'] were strings (of numbers), which was confusing CakePHP. We converted them to ints using intval() and it works perfectly now, using all the original settings...

Here's the revised version of what is in the view (notice the intval()):

        $selected = array();
        foreach ($job['Test'] as $test) {
            $selected[]= intval($test['id']);
        }
         echo $this->Form->input('Test', array('type'=>'select', 'options'=>$test_options, 'class'=>'form-control', 'multiple'=>'checkbox', 'selected'=>$selected));

Also, as a sidenote, this is evidence that pretty much everything that was challenged in the comments above works completely fine:

  1. $options['selected'] does not need to be key=>value pairs.
  2. The pattern we're using does not overwrite the request->data and passes the data to the form helper just fine.
  3. non-camelCased variable names passed to the view ($some_variable_name) are still picked up correctly by the form helper.

Hopefully this comes in handy to someone else.

0
votes

What if you pass set the default values using Model::find('list').

//controller
$this->set('test_options', $this->YourModel->find('list'));


//view
 echo $this->Form->select('Test', $test_options, array('class'=>'form-control', 'multiple'=>'checkbox'));