Relacionamentos entre Models.
No final do artigo anterior deixei um relacionamento entre os models Autor e Receita assim como o seguinte.
Veja o post anterior nesse link Views, FormHelper e Actions - Introdução ao CakePHP.
Listagem 1: relacionamento entre Receita e Autor
public $hasOne = array( 'Autor' => array( 'className' => 'Autor', ) );
A Listagem 1 não faz nada mais do que mapear o relacionamento que já existe no banco de dados.
Assim como, na modelagem, Receita tem um Autor devemos também indicar para o model que existe esse relacionamento.
O mapeamento dos relacionamentos nos permite economizar bastante código das consultas em SQL, também nos dá acesso a um objeto do model relacionado tanto dentro do próprio model ou de um controller que o mesmo pertença.
No Cake podemos mapear os seguintes tipos de relacionamentos:
1 para 1 | hasOne | Uma Receita tem um Autor |
1 para muitos | hasMany | Uma Receita tem muitos Autores |
Muitas para 1 | belongsTo | Muitas Receitas pertencem a um Autor |
Muitos para Muitas | hasAndBelongsToMany | Receita tem, e pertencem a muitos Autores. |
Um model pode conter mais de um relacionamento, do mesmo tipo ou não. Aqui, como sempre, estamos usando as normalizações do Cake, isso vai diminuir ainda mais a quantidade de escrita de código.
As configurações dos relacionamentos seguem o mesmo padrão para todos os tipos. Veja os exemplos:
Listagem 2: Relacionamento do tipo hasOne
<?php class Usuario extends AppModel { /* O trecho de código abaixo está relacionando o model Usuario ao model Perfil e com a condição do campo ativo do perfil seja igual a 1 */ public $hasOne = array( //Alias do relacionamento, geralmente usamos o nome do model 'Perfil' => array( //nome do model a ser relacionado 'className' => 'Perfil', //condições para a recuperação 'conditions' => array('Perfil.ativo' => '1'), //quando setado para true e o metodo delete() é chamado com //cascaded igual a true todas as dependências serão excluídas também 'dependent ' => false, //Array com o nome dos campos que deve ser recuperados 'fields' => array('Perfil.nome','Perfil.ativo'), //campo da chave estrangeira, por padrão esse campo deve ser o //model mais _id 'foreignkey' => “perfil_id”, //array com a ordem 'order' => array('Perfil.nome'=>'asc') ) ); } ?>
Listagem 3: Relacionamento do tipo belongsTo
<?php class Perfil extends AppModel { /* Relacionamento onde um perfil pertence a um usuario */ public $belongsTo = array( 'Usuario' => array( 'className' => 'Usuario', //chave que representa o campo do outro model 'foreignKey' => 'usuario_id', //tipo de join, left é o padrão 'type' => 'left' ) ); }
As mesmas opções do relacionamento hasOne estão presentes no belongsTo acrescidas de type, counterCache, counterScope, essas duas ultimas não são interessantes para essa serie de introdução.
Vejamos agora os relacionamentos hasMany e hasAndBelongsToMany.
Listagem 4: Relacionamentos de tipo hasMany e hasAndBelongsToMany.
<?php class Receita extends AppModel { public $hasMany = array( 'Receita' => array( 'className' => 'Receita', // limite de linhas que serão retornadas. 'limit' => '5', ) ); public $hasAndBelongsToMany = array( 'Ingrediente' => array( 'className' => 'Ingrediente', //tabela do relacionamento 'joinTable' => 'ingredientes_receitas', 'foreignKey' => 'receita_id', //chave de associação 'associationForeignKey' => 'ingrediente_id', ) ); } ?>
Nesses dois relacionamentos as opções são também praticamente as mesmas, na listagem 4 destaquei aquelas que na minha opinião são as mais importantes, as outras veremos nos próximos posts. E estivermos usando as normas “cakerianas” só precisaremos mudar alguma coisa aqui quando a regra de negócios exigir.
Agora vamos dar continuidade ao nosso CRUD.
Novas Actions para o CRUD
Nós já temos nosso cadastro pronto e acredito que vocês devem ter cadastrado algumas receitas. Vamos fazer agora uma função listagem e uma para deletar.
Voltemos ao nosso Controlador de Receita
Listagem 5: Action de listagem dos registros.
<?php class ReceitaController extends AppController(){ /* outros métodos aqui em cima */ public function lista(){ /* chamamos o métodos find('all') do model para retornar todos os registros */ $receitas = $this->Receita->find('all'); /* enviamos a variável receitas para a view pelo metodo set do controller */ $this->set('receitas',$receitas); } } ?>
Na Listagem 5 fizemos uso do método find($type,$options) do model, desta vez com o tipo “all”, para recuperar todos os registros previamente cadastrados, depois usamos o método set($one,$two) para enviarmos para a view a variável $receitas.
Espera aí que agente já faz a view, vamos contruir nossa action para deletar aí agente já faz tudo de uma vez.
Listagem 6: Action para deletar
<?php class ReceitaController extends AppController(){ /* outros métodos aqui em cima */ public function deletar($id = null){ /* verificamos se o id é igual a null e caso ele seja chamamos o método redirect do controller para redirecionar a requisição junto com uma mensagem de aviso. */ if(!isset($id)) /*Uso do component Session, coisa de outros posts*/ $this->Session->setFlash('Erro!'); /*redireciona para a acton listar*/ $this->redirect->(“/receitas/listar”); /* Criamos um novo objeto do tipo receita Isso é necessário aqui porquê vamos setar propriedades para o objeto manipular. */ $this->Receita->create(); /*setamos o id da receita*/ $this->Receita->id = $id /*chamamos a função delete do model que retorna true ou false*/ if($this->Receita->delete()) /*Uso do component Session, coisa de outros posts*/ $this->Session->setFlash('Arquivo deletado!'); }else{ $this->Session->setFlash('Não foi possível deletar!'); } /*redireciona para a acton listar*/ $this->redirect->(“/receitas/listar”); } ?>
Note que a action delete termina com um redirecionamento, dessa forma ela nunca vai renderizar uma action própria, isso significa que não precisaremos criar uma view para essa action, o que é muito natural.
View da action listar.
Para essa view temos o array que enviamos da action, então faremos um foreach para varrermos o seu conteúdo, e para esse exemplo usaremos uma table HTML.
Listagem 7: View listar
<table> <tr> <th>id</th> <th>titulo</th> <th>texto</th> <th>autor</th> </tr> <?php foreach($receitas as $receita){ ?> <tr> <td><?php echo $receita['Receita']['id'] ?></td> <td><?php echo $receita['Receita']['title'] ?></td> <td><?php echo $receita['Receita']['texto'] ?></td> <td><?php echo $receita['Autor']['name'] ?></td> </tr> <?php } ?> </table>
O padrão de arrays é o padrão do cake [Model][campo] dessa forma sempre temos uma forma intuitiva e organizada de recuperar os dados, todas as funções que recuperam dados retornam dessa forma.
Vamos implementar na listagem anterior um link para chamar na action delete, utilizando o HtmlHelper que já vem habilitado por padrão.
Listagem 7: implementação de link para a action delete
<table> <tr> <th>id</th> <th>titulo</th> <th>texto</th> <th>autor</th> <th>ação</th> </tr> <?php foreach($receitas as $receita){ ?> <tr> <td><?php echo $receita['Receita']['id'] ?></td> <td><?php echo $receita['Receita']['title'] ?></td> <td><?php echo $receita['Receita']['texto'] ?></td> <td><?php echo $receita['Autor']['name'] ?></td> <td><?php /* Html:link($string,$url,$options), similar a uma tag <a></a> A url pode ser passada como um array com o controlador, a action e as variáveis que ficaram acessíveis como parametro ou uma string da seguinte forma /controller/action/variavel Vamos usar o array no exemplo */ echo $this->Html->link('delelar', array('controller'=>”receitas”,”action”=>”delete”,$receita['Receita']['id'])) ?> </td> </tr> <?php } ?> </table>
Bem pessoal por aqui terminamos nossa serie de introdução ao CakePHP. Tem muita coisa que ele faz que vai facilitar muito nossas vidas, mas o intuito desses posts foi o de deixar claro a divisão das camadas do Bolo(Cake) , das responsabilidade de cada uma delas.
Nos próximos posts daremos focos a coisas mais especificas do cotidiano do programador que quiser enveredar-se no mundo do CakePHP.
Forte abraço e até a próxima.