Yii Model Manytomany Relation With Another 2 Models

first of all I apologize for any mistake in writing Well, let me explain

I have 3 models, the Music Model, the MusicLink Model and the MusicGenre Model. In the MusicController, i have the action "Create".

Then, on the form, I have the fields of Music Model, and 2 other fields. Select2 a widget that lets you select multiple Genres, and another field for the links (urls of the music file) that can be dynamically cloned by jQuery plugin "RelCopy" (up to a maximum of to 6 fields)

An music can have multiple genres, as well as several links

My problem is how to save all these fields, correctly? I hope I’m being clear enough.

Here is the code I have so far:

MusicController.php

[CODE]/**

  • Creates a new model.

  • If creation is successful, the browser will be redirected to the ‘view’ page.

*/

public function actionCreate()

{

$music = new Music;

$genre = new MusicGenre;

$link = new MusicLink;

// Uncomment the following line if AJAX validation is needed

// $this->performAjaxValidation($music);

if(isset($_POST[‘Music’]) && isset($_POST[‘MusicGenre’]))

{

CActiveForm::validate($genre);


CActiveForm::validate($link);


$music->attributes=$_POST['Music'];


if($music->save()){


    foreach(explode(',',$_POST['MusicGenre']['genre_id']) as $gen)


    {


        $genre=new MusicGenre;


        $genre->genre_id = $gen;


        $genre->music_id = $music->id;


        if($genre->validate())


            $valid[]=true;


        else


            $valid[]=false;


    }





    foreach($_POST['MusicLink']['links'] as $lnk)


    {


        $link=new MusicLink;


        $link->link = $lnk;


        $link->music_id = $music->id;


        $link->host = MusicLink::model()->getHoster($lnk);


        if($link->validate())


            $valid[]=true;


        else


            $valid[]=false;


    }


    if(!in_array(false,$valid)){


        $genre->save();


        $link->save();


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


    }


}

}

$this->render(‘create’,array(

‘music’=>$music,

‘genre’=>$genre,

‘link’=>$link,

));

}

/**

  • Updates a particular model.

  • If update is successful, the browser will be redirected to the ‘view’ page.

  • @param integer $id the ID of the model to be updated

*/

public function actionUpdate($id)

{

$music = $this->loadModel($id);

$genre = MusicGenre::model()->findByAttributes(array(‘music_id’=>$id));//new MusicGenre;

$link = MusicLink::model()->findAll(‘music_id=:mID’,array(’:mID’=>$id));;

// Uncomment the following line if AJAX validation is needed

// $this->performAjaxValidation($music);

if(isset($_POST[‘Music’]) && isset($_POST[‘MusicGenre’]))

{

$music->attributes=$_POST['Music'];


if($music->save()){


    foreach(explode(',',$_POST['MusicGenre']['genre_id']) as $gen)


    {


        //$genre = MusicGenre::model()->findByAttributes(array('music_id'=>$music->id));


        $genre->genre_id = Genre::model()->nameToId($gen);


        $genre->music_id = $music->id;


        $valid[]=$genre->save();


    }


    foreach($_POST['MusicLink']['links'] as $lid => $lnk)


    {


        $link = MusicLink::model()->find('music_id=:mID AND id=:lID',array(':mID'=>$music->id,':lID'=>$lid));


        if($link == null){


            $link=new MusicLink;


            $link->link = $lnk;


            $link->music_id = $music->id;


            $link->host = MusicLink::model()->getHoster($lnk);


            $valid[]=$link->save();


        }else if($link->link != $lnk){


            $link->link = $lnk;


            $link->music_id = $music->id;


            $link->host = MusicLink::model()->getHoster($lnk);


            $valid[]=$link->save();


        }


    }


    if(!in_array(false,$valid))


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


}

}

$this->render(‘update’,array(

‘music’=>$music,

‘genre’=>$genre,

‘link’=>$link,

));

}

[/CODE]

\music\_form.php



<?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm',array(


    'id'=>'music-form',


    'type' => 'horizontal',


    'customCssErrors' => 'inline',


    'enableAjaxValidation'=>false,


)); ?>


<fieldset>


<legend>


<?php echo $music->isNewRecord ? Yii::t('b2r','Create Music') : Yii::t('b2r','Update Music') ;?>


</legend>


<p  class="help-block well well-small"><?php  echo  Yii::t('b2r','Fields with <span  class="required">*</span>  are required.'); ?>


</p>


<?php


$models = array($music,$genre);


if (is_array($link))


    foreach ($link as $lnk)


        $models[] = $lnk;


else


    $models[] = $link;


//die(var_dump($models));


?>


<?php echo $form->errorSummary($models); ?>


<div class="span6 first">


    <?php echo $form->textFieldRow($music,'artist',array('class'=>'span12','maxlength'=>255)); ?>


    <?php echo $form->textFieldRow($music,'title',array('class'=>'span12','maxlength'=>255)); ?>


    <?php echo $form->textAreaRow($music,'desc',array('rows'=>3, 'cols'=>60, 'class'=>'span12')); ?>


    <?php echo $form->select2Row($genre, 'genre_id', array(


                        'asDropDownList' => false,


                        'val' => MusicGenre::model()->getMusicGenresNames($music->id),


                        'options' => array(


                            'data' => Genre::model()->getGenres(),


                            'placeholder' => 'Escreva um ou mais categorias, separadas por virgulas',


                            'containerCssClass' => 'span12',


                            'tokenSeparators' => array(','),


                            'multiple'=>true,


                            'width'=>'none',


                            'initSelection' => 'js:function (element, callback) {


                                  var val = [];


                                  $(element.val().split(",")).each(function () {


                                      val.push({id: this, text: this});


                                  });


                                  callback(val);


                            }'


                        )


                      )); ?>


    &lt;?php  echo  &#036;form-&gt;maskedTextFieldRow(&#036;music,'length',array('mask'=&gt;'99:99?:99','htmlOptions'=&gt;array('class'=&gt;'span12','maxlength'=&gt;<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/cool.gif' class='bbc_emoticon' alt='8)' />),array('hint'=&gt;Yii::t('b2r','No  formato  {f1} ou {f2}',array('{f1}'=&gt;'&lt;i  class=&quot;label  label-info&quot;&gt;MM:SS&lt;/i&gt;','{f2}'=&gt;'&lt;i  class=&quot;label  label-info&quot;&gt;HH:MM:SS&lt;/i&gt;')))); ?&gt;


    &lt;?php  echo  &#036;form-&gt;maskedTextFieldRow(&#036;music,'size',array('mask'=&gt;'?~~~.~~','charMap'=&gt;array('~'=&gt;'^[0-9]+(&#092;.)?[0-9]{0,2}  ),'placeholder'=&gt;'0','htmlOptions'=&gt;array('class'=&gt;'span12','maxlength'=&gt;<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/cool.gif' class='bbc_emoticon' alt='8)' />),array('hint'=&gt;Yii::t('b2r','No  formato  {f1} ou {f2}',array('{f1}'=&gt;'&lt;i  class=&quot;label  label-info&quot;&gt;xx.xx&lt;/i&gt;','{f2}'=&gt;'&lt;i  class=&quot;label  label-info&quot;&gt;xxx.xx&lt;/i&gt;')))); ?&gt;


&lt;/div&gt;


&lt;div class=&quot;span6&quot;&gt;


    &lt;div class=&quot;controls&quot;&gt;


        &lt;ul class=&quot;thumbnails&quot;&gt;


            &lt;li class=&quot;span5&quot;&gt;


                &lt;a class=&quot;thumbnail&quot;&gt;


                &lt;?php echo CHtml::image('/images/capa.gif',Yii::t('b2r','Previsualização da Imagem'),


                        array('id'=&gt;'previewHolder','width'=&gt;'170px','height'=&gt;'170px')); ?&gt;


                &lt;/a&gt;


            &lt;/li&gt;


        &lt;/ul&gt;


    &lt;/div&gt;


    &lt;?php  echo  &#036;form-&gt;textFieldRow(&#036;music,'image',array('class'=&gt;'span8','maxlength'=&gt;255),array('controlCss'=&gt;'skipcopy','append'=&gt;'&lt;a  href=&quot;#&quot;  id=&quot;findcover&quot; data-toggle=&quot;tooltip&quot;  title=&quot;'.Yii::t('b2r','Procurar  capa no  Google').'&quot;&gt;&lt;i  class=&quot;icon-circle-arrow-right&quot;&gt;&lt;/i&gt;&lt;/a&gt;'));  ?&gt;


    &lt;?php


    if (&#036;music-&gt;isNewRecord){


        echo &#036;form-&gt;textFieldRow(&#036;link,'link',


            array('name'=&gt;'MusicLink[links][0]','value'=&gt;'','maxlength'=&gt;255, 'class'=&gt;'span8'),


            array('controlCss'=&gt;'copy  clonable','append'=&gt;'&lt;a  id=&quot;copylink&quot; href=&quot;#&quot;  rel=&quot;.copy&quot;&gt;&lt;i  class=&quot;icon-plus&quot;&gt;&lt;/i&gt;&lt;/a&gt; ')


        );


    }else{


        &#036;last = count(&#036;link)-1;


        foreach (&#036;link as &#036;k =&gt; &#036;v)


            die(var_dump(&#036;link));


            &#036;id = (is_null(&#036;v)) ? &#036;k : &#036;v-&gt;id;


            if(&#036;k == &#036;last)


                echo &#036;form-&gt;textFieldRow(&#036;v,'link',


                    array('name'=&gt;'MusicLink[links]['.&#036;id.']','maxlength'=&gt;255, 'class'=&gt;'span8'),


                    array('controlCss'=&gt;'copy  clonable','label'=&gt;'&lt;span  class=&quot;required&quot;&gt;*&lt;/span&gt;','append'=&gt;'&lt;a  id=&quot;copylink&quot;  href=&quot;#&quot; rel=&quot;.copy&quot;&gt;&lt;i  class=&quot;icon-plus&quot;&gt;&lt;/i&gt;&lt;/a&gt;  ')


                );


            else


                echo &#036;form-&gt;textFieldRow(&#036;v,'link',


                    array('name'=&gt;'MusicLink[links]['.&#036;id.']','maxlength'=&gt;255, 'class'=&gt;'span8'),


                    array('controlCss'=&gt;'clonable')


                );


    }


    ?&gt;


&lt;?php


&#036;this-&gt;widget('ext.jqrelcopy.JQRelcopy',array(


//the id of the 'Copy' link in the view, see below.


'id' =&gt; 'copylink',


  //add a icon image tag instead of the text


  //leave empty to disable removing


'removeText' =&gt; '&lt;i class=&quot;icon-remove&quot;&gt;&lt;/i&gt;',


//htmlOptions of the remove link


'removeHtmlOptions' =&gt; array('style'=&gt;'margin-left:2px;padding:3px 10px;','class'=&gt;'btn btn-small btn-danger'),


//options of the plugin, see [url=&quot;http://www.andresvidal.com/labs/relcopy.html&quot;]http://www.andresvid...bs/relcopy.html[/url]


'options' =&gt; array(


   	//A class to attach to each copy


      'copyClass'=&gt;'newcopy',


      // The number of allowed copies. Default: 0 is unlimited


      'limit'=&gt;6,


      //Option to clear each copies text input fields or textarea


      'clearInputs'=&gt;true,


      //A jQuery selector used to exclude an element and its children


      'excludeSelector'=&gt;'.skipcopy',


      //Additional HTML to attach at the end of each copy.


      //'append'=&gt;CHtml::tag('span',array('class'=&gt;'hint'),'You can remove this line'),


        //'jsAfterNewId' =&gt; &quot;if(typeof &#036;(this &gt;  input).attr('name') &#33;==  'undefined'){ &#036;(this &gt; input).attr('name',  &#036;(this &gt;  input).attr('name').replace('new', 'new_'+counter));}&quot;,


   )


));


?&gt;


&lt;/div&gt;


&lt;?php  &#036;collapse  =  &#036;this-&gt;beginWidget('bootstrap.widgets.TbCollapse',array('htmlOptions'=&gt;array('class'=&gt;'span12  first')));  ?&gt;


    &lt;div class=&quot;accordion-group&quot;&gt;


        &lt;div class=&quot;accordion-heading&quot;&gt;


            &lt;a class=&quot;accordion-toggle&quot; data-toggle=&quot;collapse&quot;


            data-parent=&quot;#accordion2&quot; href=&quot;#collapseOne&quot;&gt;


            Avançado


            &lt;/a&gt;


        &lt;/div&gt;


        &lt;div id=&quot;collapseOne&quot; class=&quot;accordion-body collapse&quot;&gt;


            &lt;div class=&quot;accordion-inner&quot;&gt;


                &lt;?php echo &#036;form-&gt;textFieldRow(&#036;music,'bitrate',array('class'=&gt;'span9')); ?&gt;


            &lt;/div&gt;


        &lt;/div&gt;


    &lt;/div&gt;


&lt;?php &#036;this-&gt;endWidget(); ?&gt;


&lt;/fieldset&gt;


&lt;div class=&quot;form-actions&quot;&gt;


    &lt;?php &#036;this-&gt;widget('bootstrap.widgets.TbButton', array(


            'buttonType'=&gt;'submit',


            'type'=&gt;'primary',


            'label'=&gt;&#036;music-&gt;isNewRecord ? Yii::t('b2r','Create') : Yii::t('b2r','Save'),


        )); ?&gt;


&lt;/div&gt;


&lt;?php &#036;this-&gt;endWidget(); ?&gt;


&lt;?php Yii::app()-&gt;clientScript-&gt;registerScript('script', &quot;


   &#036;('#Music_image').change(function() {


   	&#036;('#previewHolder').attr('src',&#036;(this).val());


   });


   &#036;('#findcover').click(function() {


   	var q = &#036;('#Music_artist').val();


   	q += ' - '+&#036;('#Music_title').val();


   	q += ' cover';


    	window.open('https://www.google.pt/search?q='+escape(q)+'&amp;tbm=isch', '_blank');


   });


&quot;


, CClientScript::POS_READY);?&gt;

With this code, I can save everything correctly, but for example, if I do not select a Genre, or not enter a link to the file, only saves the music. But for example, if has missing a required field of Music Model, as well as MusicGenres or MusicLink model, is validated all three Models. If the errors only belong to MusicGenre or MusicLink model, the music is saved, but not these 2 Models

I'm using Yii version 1.1.14, with YiiBooster and YiiBoilerplate

Here are the links to the plugins:

http://www.andresvid…bs/relcopy.html

http://ivaynberg.github.io/select2/

http://yiibooster.clevertech.biz/

Not solved yet guys :confused: