Reputation: 25
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
/**
* 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['Music']['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['Music']['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,
));
}
\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);
}'
)
)); ?>
<?php echo $form->maskedTextFieldRow($music,'length',array('mask'=>'99:99?:99','htmlOptions'=>array('class'=>'span12','maxlength'=>8)),array('hint'=>Yii::t('b2r','No formato {f1} ou {f2}',array('{f1}'=>'<i class="label label-info">MM:SS</i>','{f2}'=>'<i class="label label-info">HH:MM:SS</i>')))); ?>
<?php echo $form->maskedTextFieldRow($music,'size',array('mask'=>'?~~~.~~','charMap'=>array('~'=>'^[0-9]+(\.)?[0-9]{0,2}$'),'placeholder'=>'0','htmlOptions'=>array('class'=>'span12','maxlength'=>8)),array('hint'=>Yii::t('b2r','No formato {f1} ou {f2}',array('{f1}'=>'<i class="label label-info">xx.xx</i>','{f2}'=>'<i class="label label-info">xxx.xx</i>')))); ?>
</div>
<div class="span6">
<div class="controls">
<ul class="thumbnails">
<li class="span5">
<a class="thumbnail">
<?php echo CHtml::image('/images/capa.gif',Yii::t('b2r','Previsualização da Imagem'),
array('id'=>'previewHolder','width'=>'170px','height'=>'170px')); ?>
</a>
</li>
</ul>
</div>
<?php echo $form->textFieldRow($music,'image',array('class'=>'span8','maxlength'=>255),array('controlCss'=>'skipcopy','append'=>'<a href="#" id="findcover" data-toggle="tooltip" title="'.Yii::t('b2r','Procurar capa no Google').'"><i class="icon-circle-arrow-right"></i></a>')); ?>
<?php
if ($music->isNewRecord){
echo $form->textFieldRow($link,'link',
array('name'=>'MusicLink[links][0]','value'=>'','maxlength'=>255, 'class'=>'span8'),
array('controlCss'=>'copy clonable','append'=>'<a id="copylink" href="#" rel=".copy"><i class="icon-plus"></i></a> ')
);
}else{
$last = count($link)-1;
foreach ($link as $k => $v)
die(var_dump($link));
$id = (is_null($v)) ? $k : $v->id;
if($k == $last)
echo $form->textFieldRow($v,'link',
array('name'=>'MusicLink[links]['.$id.']','maxlength'=>255, 'class'=>'span8'),
array('controlCss'=>'copy clonable','label'=>'<span class="required">*</span>','append'=>'<a id="copylink" href="#" rel=".copy"><i class="icon-plus"></i></a> ')
);
else
echo $form->textFieldRow($v,'link',
array('name'=>'MusicLink[links]['.$id.']','maxlength'=>255, 'class'=>'span8'),
array('controlCss'=>'clonable')
);
}
?>
<?php
$this->widget('ext.jqrelcopy.JQRelcopy',array(
//the id of the 'Copy' link in the view, see below.
'id' => 'copylink',
//add a icon image tag instead of the text
//leave empty to disable removing
'removeText' => '<i class="icon-remove"></i>',
//htmlOptions of the remove link
'removeHtmlOptions' => array('style'=>'margin-left:2px;padding:3px 10px;','class'=>'btn btn-small btn-danger'),
//options of the plugin, see http://www.andresvidal.com/labs/relcopy.html
'options' => array(
//A class to attach to each copy
'copyClass'=>'newcopy',
// The number of allowed copies. Default: 0 is unlimited
'limit'=>6,
//Option to clear each copies text input fields or textarea
'clearInputs'=>true,
//A jQuery selector used to exclude an element and its children
'excludeSelector'=>'.skipcopy',
//Additional HTML to attach at the end of each copy.
//'append'=>CHtml::tag('span',array('class'=>'hint'),'You can remove this line'),
//'jsAfterNewId' => "if(typeof $(this > input).attr('name') !== 'undefined'){ $(this > input).attr('name', $(this > input).attr('name').replace('new', 'new_'+counter));}",
)
));
?>
</div>
<?php $collapse = $this->beginWidget('bootstrap.widgets.TbCollapse',array('htmlOptions'=>array('class'=>'span12 first'))); ?>
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse"
data-parent="#accordion2" href="#collapseOne">
Avançado
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
<?php echo $form->textFieldRow($music,'bitrate',array('class'=>'span9')); ?>
</div>
</div>
</div>
<?php $this->endWidget(); ?>
</fieldset>
<div class="form-actions">
<?php $this->widget('bootstrap.widgets.TbButton', array(
'buttonType'=>'submit',
'type'=>'primary',
'label'=>$music->isNewRecord ? Yii::t('b2r','Create') : Yii::t('b2r','Save'),
)); ?>
</div>
<?php $this->endWidget(); ?>
<?php Yii::app()->clientScript->registerScript('script', "
$('#Music_image').change(function() {
$('#previewHolder').attr('src',$(this).val());
});
$('#findcover').click(function() {
var q = $('#Music_artist').val();
q += ' - '+$('#Music_title').val();
q += ' cover';
window.open('https://www.google.pt/search?q='+escape(q)+'&tbm=isch', '_blank');
});
"
, CClientScript::POS_READY);?>
EDIT
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 YiiBoostrap and YiiBoilerplate
Here are the links to the plugins:
http://www.andresvidal.com/labs/relcopy.html
http://ivaynberg.github.io/select2/
http://yiibooster.clevertech.biz/
Upvotes: 1
Views: 370
Reputation: 11709
You may use esaverelatedbehavior component developed by sluderitz for saving related models, you can download it here
After installing above extension you can do like this
$music->attributes=$_POST['Music'];
if (isset($_POST['MusicGenre']))
{
$music->musicgenre= $_POST['MusicGenre'];
}
if ($music->saveWithRelated('musicgenre'))
$this->redirect(array('view', 'id' => $model->id));
Note: We are able to do $music->musicgenre
because musicgenre
is a relation name.
A detailed solution for saving multiple related models using above extension is available in my blog post.
Upvotes: 1
Reputation: 355
Ok, now I know wat you want.
In the create method, when you test if the array of generes or links are valid you can generate an error message and redirect to the update action and ask to the user to insert the generes or links data.
In the actionCreate:
if($music->save()){
......
if(!in_array(false,$valid)){
$genre->save();
$link->save();
$this->redirect(array('view','id'=>$music->id));
}
else {
Yii::app()->user->setFlash('error',"Some cool warning message");
$this->redirect(array('update','id'=>$music->id)));
}
}
......
In the ActionUpdate do the same:
........
if(!in_array(false,$valid))
$this->redirect(array('view','id'=>$music->id));
else
Yii::app()->user->setFlash('error',"Some cool warning message");
Now, in the update view file (not in the _form) you must show the error message:
<?php if(Yii::app()->user->hasFlash('error')):?>
<div class="flash-error">
<?php echo Yii::app()->user->getFlash('error'); ?>
</div>
<?php endif; ?>
There's a default CSS defined fot the "flash-error" DIV. See enter link description here.
I hope it helps you.
ANOTHER POSSIBLE SOLUTION can be definig an array for the generes and links into music model.
then, assign the form's values in these variables befor calling $music->saave.
In the music model you must write validaors rules for the arrays and in beforeSave() afterSave() method write the code to insert the genere and links rows.
Upvotes: 0