Guardar E Validar 3 Models Relacionados Com Yii Framework

Bem, vou tentar aqui a minha sorte, espero encontrar alguem que me consiga ajudar

Então é assim, tenho 3 models:

Music

MusicGenre

MusicLink

ambos models correspondem a 3 tabelas, sendo a MusicGenre e MusicLink, tabelas auxiliares, que fazem a ligação entre a tabela Music, e as tabelas Genres e Links, respetivamente, isto porque uma Música pode ter varios links de ficheiros, bem como ter varios estilos.

no MusicController tenho a action create e update, entre outras

no form associado a essas actions, tenho para alem dos campos da tabela/model music mais 2 campos, um campo para escolher os genres musicais, o que existe aqui no forum, quando se cria um topico, e permite meter varias tags, usando o Select2, que envia os ids dos generos por post, num unico indice, em que os valores são separados por virgulas (ex: MusicGenre[genre_id] = 1,7,32,4)

tenho depois um outro campo de texto, que corresponde ao link da musica, sendo que este campo pode ser clonado até ter 6 copias, usando o plugin RelCopy, e envia varios links, caso existam, por exemplo: MusicLink[link][0] = http://servidor.com/musica1.mp.3, MusicLink[link][1] = http://servidor.com/musica2.mp.3

O meu problema está em como guardar estes dados correctamente ?

até agora, tenho este codigo:

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;

O codigo funciona, mas tem alguns "bugs".

imaginando que se tenta enviar o form, a faltar algum campo obrigatorio do model Music e os 2 campos dos models MusicLink e MusicGenre por preencher, ele ai valida os 3 models, e não guarda nenhum.

Mas se preencher todos os campos do model Music e deixar por preencher algum campo dos models MusicLink ou MusicGenre, ele ai guarda a musica, e não consigo fazer com que ele valide os outros 2 models alem do MUSIC e que retorne para o form, como é o habitual…

não sei que consegui explicar bem, isto ta-me a matar o juiso…

Estou a usar a Yii 1.1.14, com o YiiBoilerplate e Yiibooster

ficam aqui os links para os plugins usados e mensionados…:

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

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

http://yiibooster.clevertech.biz/

nao sei se percebi bem o teu problema. mas penso ke seja relativo a gravar multiploes models ao mesmo tempo.

para guardar multiplos records em diferentes models que tenham relacao entre eles… axo ke é sempre bom usar transactions

$transaction = Yii::app()->db->beginTransaction();

	try


	{


		// todo o codigo aki relativo ao  save de models			


		


		&#036;transaction-&gt;commit();





                  // weee tudo bom <img src='http://www.yiiframework.com/forum/public/style_emoticons/default/biggrin.gif' class='bbc_emoticon' alt=':D' />


	}


	catch (Exception &#036;ex)


	{


                   // occoreu um problem, dump the &#036;ex-&gt;getErrors()


		&#036;transaction-&gt;rollback();


	}

sim é de certa forma isso :unsure:

vou testar esse codigo assim que puder, e depois dou feedback.

desde já muito obrigado, ando a ficar minado com isto :lol:

edit:

não funciona, ele insere na mesma a musica…

eu não insiro genero nem nenhum link, e são campos obrigatorios… ele simplesmente não valida :S

basicamente isto é como se fosse um post(artigo) que pode ter várias categorias… ja procurei varios exemplos na wiki mas ou não consigo implementar, ou não sao bem o que pretendo…


$transaction = Yii::app()->db->beginTransaction();


try

{

	$valid[] = $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();

    	$transaction->commit();

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

	}


}

catch (Exception $ex)

{

// occoreu um problem, dump the $ex->getErrors()

$transaction->rollback();

}

Tenho uma extensão que fiz e poderá resolver o seu problema, caso queira me envie um email ao ulisses.vaquero@gmail.com

Não disponibilizei ainda no grupo pois estou bloqueado.

Nada me está a resultar, é um facto :expressionless: