HUFERSIL.WEBDEVELOPER - Soluções com qualidade - Hugo Ferreira da Silva

Relacionamentos 1-N e M-N Ter, 14 de julho de 2009 - as 19h01 - 11 comentários

Como havia dito antes, vou mostar um exemplo de uso com relacionamentos.
Neste exemplo, estou assumindo que:

O objetivo é mostrar como os relacionamentos podem ser criados com Lumine, de forma rápida e orientada a objetos.

Iniciando

Antes de prosseguir, é importante que você tenha seu banco de dados criado e populado.
Abaixo estão os dados que vamos usar como exemplo:

-- MySQL dump 10.13  Distrib 5.1.34, for Win32 (ia32)
--
-- Host: localhost    Database: lumine
-- ------------------------------------------------------
-- Server version	5.1.34-community

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `modulo`
--

DROP TABLE IF EXISTS `modulo`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `modulo` (
  `idmodulo` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `nome` varchar(100) NOT NULL,
  `url` varchar(100) NOT NULL,
  PRIMARY KEY (`idmodulo`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `modulo`
--

LOCK TABLES `modulo` WRITE;
/*!40000 ALTER TABLE `modulo` DISABLE KEYS */;
INSERT INTO `modulo` VALUES (1,'Home','index.php'),(2,'Administracao','admin.php'),(3,'Mais um modulo legal','legal.php');
/*!40000 ALTER TABLE `modulo` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `pessoa`
--

DROP TABLE IF EXISTS `pessoa`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `pessoa` (
  `idpessoa` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `nome` varchar(100) NOT NULL,
  `email` varchar(100) NOT NULL,
  `data_cadastro` datetime NOT NULL,
  PRIMARY KEY (`idpessoa`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `pessoa`
--

LOCK TABLES `pessoa` WRITE;
/*!40000 ALTER TABLE `pessoa` DISABLE KEYS */;
INSERT INTO `pessoa` VALUES (1,'Hugo Ferreira da Silva','hufersil@gmail.com','2009-07-13 16:11:56'),(2,'Mirian Brisch da Silva','mirian@gmail.com','2009-07-13 13:19:07'),(3,'João Francisco','joao@gmail.com','2009-07-13 13:19:31'),(4,'Juliano','juliano.polito@247id.com.br','2009-07-13 13:06:19'),(5,'Macarena','macarena@gmail.com','2009-07-13 13:18:04');
/*!40000 ALTER TABLE `pessoa` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `pessoa_modulo`
--

DROP TABLE IF EXISTS `pessoa_modulo`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `pessoa_modulo` (
  `idpessoa` int(10) unsigned NOT NULL,
  `idmodulo` int(10) unsigned NOT NULL,
  PRIMARY KEY (`idpessoa`,`idmodulo`),
  KEY `FK_pessoa_modulo_2` (`idmodulo`),
  CONSTRAINT `FK_pessoa_modulo_1` FOREIGN KEY (`idpessoa`) REFERENCES `pessoa` (`idpessoa`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `FK_pessoa_modulo_2` FOREIGN KEY (`idmodulo`) REFERENCES `modulo` (`idmodulo`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `pessoa_modulo`
--

LOCK TABLES `pessoa_modulo` WRITE;
/*!40000 ALTER TABLE `pessoa_modulo` DISABLE KEYS */;
INSERT INTO `pessoa_modulo` VALUES (1,1),(2,1),(1,2),(3,2),(1,3);
/*!40000 ALTER TABLE `pessoa_modulo` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `telefone`
--

DROP TABLE IF EXISTS `telefone`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `telefone` (
  `idtelefone` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `numero` varchar(20) NOT NULL,
  `idpessoa` int(10) unsigned NOT NULL,
  PRIMARY KEY (`idtelefone`),
  KEY `FK_telefone_1` (`idpessoa`),
  CONSTRAINT `FK_telefone_1` FOREIGN KEY (`idpessoa`) REFERENCES `pessoa` (`idpessoa`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `telefone`
--

LOCK TABLES `telefone` WRITE;
/*!40000 ALTER TABLE `telefone` DISABLE KEYS */;
INSERT INTO `telefone` VALUES (1,'7777-7777',1),(2,'6666-6666',1),(3,'5555-5555',2),(4,'4444-4444',1);
/*!40000 ALTER TABLE `telefone` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2009-07-14 21:27:05


Agora, faça uma engenharia reversa do banco de dados.

Exemplificando - relacionamentos N-M

Tenha sempre em mente que o objeto principal que está sendo usado é sua fonte primária de dados.
Por exemplo: supondo que você quer todos os módulos que uma pessoa tem acesso, seu objeto principal será o que representa a tabela modulo (quero todos os módulos de uma pessoa).

Em nosso exemplo, existe um relacionamento N-M entre pessoa e módulo, sendo representado pela tabela pessoa_modulo. Se você notar, a engenharia reversa não criou a entidade para esta tabela (a não ser que você tenha escolhido para que ela gerasse). Isso porque Lumine gerencia automaticamente os dados que são gravados nesta tabela, através do relacionamento mapeado nos objetos Pessoa e Modulo.

Então, uma exemplo de consulta para este caso seria:

// altera o alias de modulo
$modulo->alias('m')
	// une com modulo - automaticamente lumine ira unir 
	// com a tabela pessoa_modulo
	->join($pessoa,'inner','p')
	// forcamos pegar somente os dados do modulo
	->selectAs()
	// indica de qual pessoa queremos os modulos
	->where('p.idpessoa = ?', 1)
	// efetua a consulta
	->find();

 

Agora, supondo que você quer o inverso: todas as pessoas de um determinado módulo, seu objeto principal seria o objeto pessoa, logo:

$pessoa->alias('p')
	// une com modulo - automaticamente lumine ira unir 
	// com a tabela pessoa_modulo
	->join($modulo,'inner','m')
	// forcamos pegar somente os dados de pessoa
	->selectAs()
	// indica de qual modulo queremos as pessoas
	->where('m.idmodulo = ?', 1)
	// efetua a consulta
	->find();


Exemplificando - relacionamentos 1-N

Aqui já é mais fácil porque você tem um relacionamento direto entre as tabelas que contém os dados.
Supondo que você quer pegar todos os telefones e o nome de cada número:

// muda o alias de telefone
$telefone->alias('t')
	// une com um novo objeto de pessoa
	->join(new Pessoa(),'inner','p')
	// pega o nome e numero
	->select('p.nome, t.numero')
	// ordena pelo nome
	->order('p.nome')
	// efetua a consulta
	->find();


Encadeamento de chamadas

Em alguns casos você precisará fazer joins aninhados, de várias classes diferentes. Lumine permite que você faça isso.
Abaixo um exemplo simples. Obs.: Este exemplo é somente para mostrar que é possível fazer encadeamento de chamadas. Na vida real, este exemplo não traria dados relevantes:

// altera o alias de modulo
$modulo->alias('m')
	// une com pessoa... ja mudando seu alias
	->join($pessoa->alias('p')
		// une pessoa com telefone
		->join($telefone,'inner','t')
	)
	// efetua a consulta
	->find();

Espero que estes pequenos exemplos ajudem a melhorar o conhecimento e utilização de Lumine.

E os arquivos encontram-se disponíveis para download.

@braços e fiquem com Deus!

Comentários

Por Eloir c enviado em 07 de junho de 2011, as 18:28 Duvida quanto join inverso em relacionamentos M-N
Tenho o seguinte relacionamento

Noticia(idnoticia, titulo, texto)
Noticia_PalavrasChave(idnoticia, idpalavrachave)
PalavrasChave(idpalavrachave, palavra)


select
ng.*
from Noticia as Noticia

inner join Noticia_PalavrasChave as Noticia_PalavrasChave
on Noticia_PalavrasChave.id_noticia = Noticia.id_noticia

inner join PalavrasChave as palavraschave
on palavraschave.id_palavra_chave = Noticia_PalavrasChave.id_palavra_chave

inner JOIN Noticia_PalavrasChave as pcn
on pcn.id_palavra_chave = palavraschave.id_palavra_chave

inner join Noticia as ng
on ng.id_noticia = pcn.id_noticia

where Noticia.id_noticia = 552


Porque desta sql Gigantesca e estranha?
Bom, porque eu quero pegar todas as noticias que tem as mesmas palavras chave da noticia em questão


fiz os relacionamentos, os joins e tudo, uso o Lumine a um bom tempo, porém como ele não adiciona um alias nas tabelas intermediárias N-M então diz que estou consultando duas vezes a tabela com o mesmo nome:
Not unique table/alias: ......
Por Junio de Almeida enviado em 23 de novembro de 2010, as 11:23 Hugo, há alguma maneira de insetir registros na tabela de união quando o relacionamento é M:N de forma automatica? Tipo, tenho duas tabelas, uma de carro outra de caracteristicas do carro. Quando for inserir o carro, terei também um array de codigos de caracteristicas que seria inserido na tabela de união entre carro e caracteristica. Existe alguma forma pratica de fazer este insert?
Por Daniel Schultz enviado em 29 de outubro de 2009, as 19:23 Uma dúvida bem doida.

Tenho uma tabela fontegeradora e uma tabela risco. Unindo estas tabelas em N-M, tenho a fontegeradora_risco.

A tabela associativa terá id_fontegeradora e id_risco.

Mas tenho que manter um histórico, ou seja, terei que colocar um campo ativo=0/1 e um campo data_encerramento.

Tenho usado o lazy=true para retornar os dados associados, mas surgiu a duvida:

Como usar o lazy true e resgatar somente onde ativo=1 ?

Posso fazer um join comum, mas queria usar o lazy, justamente pelo fator "lazy" :)

Grato pela versão 1.5.2!
Por Hugo Ferreira da Silva enviado em 18 de outubro de 2009, as 19:36 ele adiciona tanto sufixo como prefixo.
É só mudar a posicao do %s

prefixo_%s
%s_sufixo

@braços
Por Daniel Schultz enviado em 18 de outubro de 2009, as 18:35 Uma opinião...

O selectAs() adiciona um sufixo, porque não fazer um adicionador de prefixo?
Por Daniel Schultz enviado em 18 de outubro de 2009, as 17:50 Hugo, isso mesmo! Grato pela ajuda, estou construindo um sistema para a área médica no meu tempo livre, e estou usando o Lumine como ferramenta.
Por Hugo Ferreira da Silva enviado em 18 de outubro de 2009, as 11:24 Você pode fazer o seguinte: remove todos e cadastra novamente os que você quer que fique. Provavelmente isso vem como um array por post, então:

$obj->setores = empty($_POST['setores']) ? array() : $_POST['setores'];
$obj->removeAll('setores');
$obj->save();
Por Daniel Schultz enviado em 18 de outubro de 2009, as 01:22 vi algo com a funcao remove, mas como usá-la assim?

quero remover todos que nao forem aqueles que passei.

porque a remove() atual você tem que especificar os ids a remover, e eu só estou passando aqueles que eu quero que continuem, entende ?
Por Daniel Schultz enviado em 18 de outubro de 2009, as 01:16 Uma pergunta complicada Hugo.

tenho uma tabela Funcao, uma Setor

e uma setor_funcao

Estou cadastrando funcoes que contém N setores. Tudo ok.

Ai vou editar uma funcao que tem 2 setores alocados (dentro de setor_funcao)

e removo um deles da lista e dou um save, sendo que passo somente o setor restante.

Como fazer o lumine entender que tem que apagar aquele removido da lista ? Ou terei que fazer manualmente ?

O exemplo está abaixo, no insert ele foi OK, mas no update, quando mandei somente 1 setor (e nao dois, como quando cadastrei), ele nao removeu o que sobrou.

if (is_numeric($_POST[registro])) $obj->get($_POST[registro]);
$obj->nome=$_POST[nome];
foreach ($_POST['setorassoc'] as $dado) $obj->setores[]=$dado;
Por Hugo Ferreira da Silva enviado em 04 de agosto de 2009, as 21:53 Leandro,

Após efetuar um get ou find, você pode iterar com o metodo fetch entre os registros.

Dê uma lida em

http://www.hufersil.com.br/lumine/fetch
Por Leandro enviado em 02 de agosto de 2009, as 23:25 Como faz para pegar os campos depois da consulta??

Deixe seu comentário