Fala aí Júnior, me desculpe… não tinha visto sua pergunta antes.
Resposta rápida:
Model = 1 para cada tabela
Controller = apenas 1
Resposta completa:
Model = Ele serve para criar as regras específicas daquela tabela, tal como campos chave, relacionamento de chaves estrangeiras, regras de consistência (tamanho de entrada, se é campo numérico…) e serve também para realizar conversão de dados, como por exemplo converter campos de Moeda (vírgula e casas decimais) e campos data no formato dd/mm/aaaa antes/depois de comunicar com o Banco de Dados. Ou seja, ele serve como interpretador e auxiliar de comunicação com o seu BD.
Controller = É específico para cada aplicação (tela) que você faça. Ou seja, no caso de criar um cadastro de Pedidos+Itens você precisa apenas de 1 controller que faça a manipulação dos 2 models (pedido e itens).
Para te explicar com mais detalhe, vamos interpretar a URL base de uma aplicação Yii:
http://localhost/SEUSITE/index.php?r=SEUCONTROLLER/create
Ou seja, no caso acima … o Controller será chamado com a função (action) create. Da mesma forma acontece com o Index, Admin, Update, View … mas nada impede de você criar outras.
Dentro de cada action, você poderá manipular quantos Models quiser, da forma que achar melhor.
Vamos à um exemplo. Aqui tenho um exemplo do início da função create no meu Controller Vendas:
public function actionCreate()
{
$model_cabec=new Vendas01;
$model_itens=new Vendas02;
$model_pgtos=new Contasreceber;
Ou seja, nele trabalho 3 models. Cabeçalho, Itens e Pagamentos.
No final da função CREATE, eu envio os 3 Models para a página:
$this->render('create',array(
'model_cabec'=>$model_cabec,
'model_itens'=>$model_itens,
'model_pgtos'=>$model_pgtos,
));
A página create.php será chamada recebendo estes models. Esta por sua vez, chama (partialrender) o formulário (_form.php), que também deverá passar/receber os 3 models. E por aí vai… se você tiver outras variáveis que necessita na página, basta passar na sequência do array do render. Assim:
$this->render('create',array(
'model_cabec'=>$model_cabec,
'model_itens'=>$model_itens,
'model_pgtos'=>$model_pgtos,
'dp_itens'=>$dp_itens,
'dp_pgtos'=>$dp_pgtos,
'readonly'=>$readonly,
));
Um detalhe importante para você entender o lance do Master -> Detail:
Repare que no Render, ele envia 1 instância de cada model. Cada $model_xxxx refere-se à um registro apenas. Para o cabeçalho tá ótimo, pois ele só usa 1 registro mesmo, já que é o MASTER. Mas para os Itens ou Pagamentos… você terá que usar uma das lógicas já enviadas, pois precisará criar múltiplas instâncias do mesmo model.
Nos exemplos da Entrada Tabular, isso é feito em etapas:
-
Defina um array, no qual você possa enviar (do controller) e receber (da página). $ITEMS. Este array terá o conteúdo da sua tabela Itens. Lembre-se da dica do render.
-
Na página (_form.php por exemplo), você terá que transformar esse array em campos <input>, pois estes campos serão lidos no seu controller mais tarde:
<?php foreach($items as $i=>$item): ?>
<tr>
<td><?php echo CHtml::activeTextField($item,"[$i]name"); ?></td>
<td><?php echo CHtml::activeTextField($item,"[$i]price"); ?></td>
<td><?php echo CHtml::activeTextField($item,"[$i]count"); ?></td>
</tr>
<?php endforeach; ?>
Uma dica importante: Utilize "name[$i]" ao invés de [$i]name, como está no exemplo.
- No seu controller, quando precisar recuperar os dados da página, basta você fazer outro foreach para ler os dados do $_POST. Daí o motivo de ter criado os campos <input> ( CHtml::activeTextField ).
( ... )
foreach($items as $i=>$item)
{
if(isset($_POST['Item'][$i]))
$item->attributes=$_POST['Item'][$i];
( ... )
- De posse dos dados no seu controller, você pode validar, gravar e etc.
$item->validate();
$item->save();
Sacou que o $item é cada instância do seu model ??
- Para adicionar novos Itens, você poderá usar o método Clone (já enviado anteriormente) ou usar Ajax, Javascript, JQuery … como preferir. O importante é conseguir manipular o seu Array e gerar uma sequência de <input> para cada registro.
Eu criei um pequeno (sub)formulário com os campos Produto, Quantidade e Valor, além de um botão Ajax. Neste Ajax faço validações adicionais e ao final insiro novos registros no array. Lembre-se novamente do Render para devolver a atualização para a página.
- No exemplo citado ele usa uma função, que não possui o código fonte. Segue abaixo o código fonte dela:
$items=$this->getItemsToUpdate();
public function getItemsToUpdate(){
$result = array();
foreach ($_POST['Item'] as $v) {
$result[$v] = new Item();
}
return $result;
}
A lógica é mais trabalhada do que eu coloquei aqui… precisa criar regras de segurança tipo Rollback, Commit… mas quis tentar explicar basicamente a lógica do Master -> Detail. Na internet tem exemplos disso, mas abaixo vou colocar um modelo que estou seguindo:
( ... )
if(isset($_POST['Item']))
( ... )
if ($model_cabec->validate()) {
$transaction = Yii::app()->db->beginTransaction();
$success1 = $model_cabec->save(false);
( ... aqui você pode fazer o loop do seu array itens .. )
( ... fazer a validação de cada instância e tentar gravá-lo no banco ... )
( ... crie um flag $success2 que armazene um FALSE se houver problema em quaiser uma das instâncias ... )
( ... depois encerre a gravação ... )
// Verificando o resultado das validações
$success = $success1 && $success2;
// Se processou tudo corretamente, então grava no BD
if ($success) {
$transaction->commit();
// redireciona para visualização
$this->redirect(array('view','id'=>$model_cabec->id));
} else {
$transaction->rollBack();
Yii::app()->user->setFlash('warning', 'Venda não registrada !');
}
}
( ... )
E agora, consegui te explicar melhor ?? Conseguiu entender a lógcia ? O post ficou longo, então posso ter confundido ou esquecido algo.
Obs: Newerton, meu guru !! Obrigado por todas as dicas. Você é responsável por 90% dessas informações !