Many to many request for tutorial

Hello,

I’m not a fulltime php-dev, and in my first Yii project, I ran into the following problem for which I couldn’t find a complete tutorial anywhere.

Discussions, arguments on how to hacks, loads of them, but can someone please take the time to explain from A to Z how to implement many-to-many data and update it in a form?

My example: I have 3 Models: Coworker <- CoworkerLanguage -> Language

The second one is the pivot, many-to-many-table or whatever you may call it.

I have succeeded in CRUD for both Coworker and Language.

But what I want is a Listbox in the Coworker form.php in which I can select multiple languages, which in turn would be updated in the CoworkerLanguage.

Additional:

This works for small lists like languages, but what if I would like to add this with a textfield which let me search with autocomplete and a plus-button next to it to create the relation?

I can’t believe there is not a soul out there that hasn’t run into this problem before.

So please, explain

Thank you in advance.

Topic moved.

Hi.

You can use CAdvancedArBehavior and activeListBox.

It will take only a few lines of code to make it working.

In model "A":




public function behaviors() {

    return array('CAdvancedArBehavior' => array(

        'class' => 'application.extensions.CAdvancedArBehavior')

    );

}


public function relations() {

    return array(

        'b' => array(self::MANY_MANY, 'B', 'AB(aId, bId)'),

    );

}



In view:


CHtml::activeListBox($model, 'b', CHtml::listData($bList, 'id', 'name'), array('multiple' => 'multiple'));

(you should change "a" and "b" to actual model and relation names).

Note the relation name in activeListBox, instead of attribute.

Ok, so this is what I have:

relation Model Coworker:




'languages'=>array(self::MANY_MANY, 'Language','tbl_coworker_language(coworker_id, language_id)')



and also off course, the function behaviors() like in the example of cadvancedArBehavior

relation in the Model Language:




'cowokers'=>array(self::MANY_MANY, 'Coworker','tbl_coworker_language(language_id, coworker_id)')



in the Controller’s actionUpdate function :




if(isset($_POST['Coworker']['languages']))

	$model->languages = $_POST['Coworker']['languages'];

			

if($model->save())

	$this->redirect(array('view','id'=>$model->id));



in the _form.php:




echo $form->dropDownList(

	$model, 

	'languages', 

	$model->getLanguageOptions(), 

	array('multiple'=>'multiple','size'=>10));



With this, I can read out all the language values into the dropDownList, including the current from the pivot tbale which are highlighted.

( I can’t use the activeListBox, because at the moment, my label isn’t called ‘name’, and that apparantly gives an other error. But that’s beside the point here anyway)

In the CoworkerController’s actionUpdate, I can access the new selected values like this: print_r($model->languages);

But when I save the form, non of them are updated or inserted.

Then why isn’t this working?

You should add ‘languages’ to “safe attributes” also.

Btw, this




if(isset($_POST['Coworker']['languages']))

$model->languages = $_POST['Coworker']['languages'];



is not required, because behavior does all the stuff.

like so:




public function rules() {

    return array(

        array('languages', 'safe')

    );

}



Aah, that last one did the job!

Thank you so much.

You know what, I’m going to write out this complete problem into a tutorial, because I’ve seen too many people struggling with this.

But then , I still got this…

The dropdown list works for small lists like languages, but what if I would like to add this with a textfield which let me search with autocomplete and a plus-button next to it to create the relation?

Example: I have Activities which could have multiple Coworkers, only there are like 15000 of them. I would like to use an auto complete textfield and a button or so to add them to a list.

Sounds reasonable.

Any idea on how to handle that last problem?

It’s up to you.

  • Hidden field ‘languages’+ some javascript to collect values on row add/remove

  • Hidden field for each new row

  • AJAX request on row add/remove (if you already have an ID of parent record)

Btw, take a look at jquery.dynamic-form, it can help.

As for me, I would go with ajax + jsviews (ex "jquery templates"), but this is kinda tricky.

Thanks, I’ll give that a try.