Pra quem é persistente sempre acha a solução.
Usando beforeValidate do CActiveForm, consegui com insatisfação fazer funciona, não queria que funciona-se do jeito que fiz, mais é a única solução que me veio em mente.
Vamos ao passo a passo:
1º Passo: Alterar o Controller.
Exemplo de como deve ficar.
public function actionCreate() {
$model = new Noticia;
$this->performAjaxValidation($model);
if (isset($_POST['Noticia'])) {
$model->attributes = $_POST['Noticia'];
if ($model->validate()) {
if ($model->save()) {
$json['id'] = $model->primaryKey;
$json['urlFileUpload'] = url(Yii::app()->controller->module->id . '/' . Yii::app()->controller->id . '/upload', array('id' => $model->primaryKey, 'class' => get_class($model), 'tipo' => 'imagem'));
Yii::app()->user->setFlash('success', t('Adicionado com sucesso'));
if (!Yii::app()->request->isAjaxRequest)
$this->redirect(array('index'));
else
$json['redirect'] = url(Yii::app()->controller->module->id . '/' . Yii::app()->controller->id . '/index');
}
}
}
if (!Yii::app()->request->isAjaxRequest)
$this->render('create', array('model' => $model));
else
echo json_encode($json);
}
Obs.: Nas estrutura de condição, eu verifico se estou ou não usando ajax, assim se na hora de validar o formulário e não tiver nenhuma imagem selecionada, ele submete o form sem ajax.
2º Passo.
Alteração do _form.
Eu criei uma função em javascript, que será usado no beforeValidate() no componente do formulário, que por padrão é o CActiveForm(), como eu estou usando o TbActiveForm(), as funções são padrão para os dois.
Está função é nativa no componente, então deve funcionar nas versões em várias versões do framework.
A função básicamente, ela recebe a resposta da validação padrão do formulário, se houver erro o formulário não é submetido, enquanto não ser preenchido os campos obrigatório que foi configurado no seu Model. Agora se não houver erro, ele vai buscar a quantidade de fotos adicionada no plugin jQuery File Upload, se a quantidade for maior que 0(zero) ele submete o formulário em $.ajax(), senão ele submete normal, atualizando a página.
Exemplo da função:
Yii::app()->clientScript->registerScript('_form', "
/**
* Chamando o plugin fileupload
*/
$('#fileupload').fileupload({
url: '',
sequentialUploads: true,
autoUpload: false,
formData: function(){ return false; }, //Removo o envio dos dados do formulário, evitando duplicação de dados.
maxFileSize: 2000000, // 2 MB
loadImageMaxFileSize: 20000000, // 20MB
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
maxNumberOfFiles: 5,
messages: {
maxNumberOfFiles: '<?php echo t('O número máximo de arquivos excedeu'); ?>',
acceptFileTypes: '<?php echo t('Tipo de arquivo não permitido'); ?>',
maxFileSize: '<?php echo t('O arquivo é muito grande'); ?>',
minFileSize: '<?php echo t('O arquivo é muito pequeno'); ?>'
}
});
/**
* Validando imagens com nomes repetidos.
*/
$('#fileupload').bind('fileuploadadd', function(e, data) {
var currentfiles = [];
$(this).fileupload('option').filesContainer.children().each(function() {
if ($(this).data('data') !== undefined) {
currentfiles.push($(this).data('data').files[0].name);
} else {
if ($(this).find('span.preview img').attr('src') !== undefined)
currentfiles.push($(this).find('span.preview img').attr('src').split('/').pop());
}
});
data.files = $.map(data.files, function(file, i) {
if ($.inArray(file.name, currentfiles) >= 0) {
return null;
}
return file;
});
});
var btnClicked; //Variavel que detecta qual botão do formulário foi clicado, para caso tenha mais de 1 botão salvar (Ex.: Salvar e Adicionar novo, Salvar e Continua Editando,...)
jQuery('div.form-actions button').live('click', function(){
btnClicked = $(this).val();
});
function sendForm(form, data, hasError){
if(hasError){
// Se houver erro, e feito uma varredura nos campos retornado, e adicionado .focus() no primeiro campo obrigatorio.
jQuery.each(data, function(index, value){
jQuery('#' + index).focus();
return false;
});
} else {
// Está variavel, pega a quantidade de imagens adicionado ao jQuery File Upload.
var countImagens = $('#fileupload .template-upload:not(\'.error\')').length;
// Se houver imagens, é enviado os dados do formulário em ajax.
if(countImagens > 0){
//console.log(btnClicked); //Debug no console para identificar qual botão foi clicado.
jQuery.ajax({
'type':'POST',
'url': form.get(0).action,
'dataType':'json',
'cache':false,
'data':form.serialize() + '&botao=' + btnClicked,
'beforeSend':function(){
//Este plugin foi somente um efeite, para bloquear a tela do usuário, caso tenha alguma lentidão no envio dos dados, assim ele não cancela o formulário ou clica novamente em salvar.
//http://www.malsup.com/jquery/block/
$.blockUI({ message: '<div class=\"progress progress-striped active\"><div class=\"bar\" style=\"width: 100%;\"></div></div>', css: { border: 'none', background: 'transparent'} });
},
'success':function(data){
//console.log(data); //Debug no console para visualizar se os dados retornado do actionIndex() está correto.
//Desativo o bloqueio da tela
$.unblockUI();
// Adiciono a URL de redirecionamento à uma váriavel para ser usando no final do envio das imagens.
var redirect = data.redirect;
//Alterado as opções do plugin
$('#fileupload').fileupload('option', {
url: data.urlFileUpload, //Alterando a URL para que seja enviado as imagens para o action() correto.
stop: function(e,data){
$('button.cancel').trigger('click'); //Este trigger, ele dispara para o botão "Cancelar Envio" do plugin, para que seja limpado o container, quando existir imagens com erro.
location.href=redirect; //Redirecionar o formulário depois de enviar todas as imagens.
}
});
},
error: function(jqXHR, textStatus, errorThrown){
$.unblockUI();
alert(jqXHR.responseText);
}
});
return false;
}
//Se não houver imagens no container do plugin, e submetido o formulário normalmente
return true;
}
}
");
Último passo:
Adicionar o ‘clientOptions’ ao componente do formulário.
$form = $this->beginWidget('bootstrap.widgets.TbActiveForm', array(
'id' => 'index-form',
'type' => 'horizontal',
'inlineErrors' => false,
'enableAjaxValidation' => true,
'clientOptions' => array('validateOnSubmit' => true, 'validateOnChange' => true,
'afterValidate' => 'js:sendForm'
),
'htmlOptions' => array(
'enctype' => 'multipart/form-data'
),
));
Para quem ta usando o ‘CActiveForm’, adiciona só a linha do ‘clientOptions’.
Para quem for usar este plugin jQuery File Upload, de preferência aos arquivos originais, não usa do YiiBooster, por que ele cria um formulário em volta do container, dando problema caso você adicione dentro de outro formulário.
Já os arquivos do site do plugin, você tem mais controle no layout, fica a dica.
Obs.: Se ainda não usou o plugin, primeiramente baixa ele do site, instala, configura, faz funcionar. Depois faça esta alteração, as dúvidas vai ser relacionada somente a questão de que no cenário create/insert não ter como enviar imagens depois de submeter o formulário, se você estiver enviando imagem somente no cenário update/edit esta explicação não vai lhe servir.
Plugin: http://blueimp.github.io/jQuery-File-Upload/