Doctrine suporta quase todos os tipos de relacionamentos de banco de dados desde um simples um-pra-um até um relacionamento de auto-referencia. Vou mostrar como definir e trabalhar com 2 tipos básicos 1:1 e 1:N.
Usando o setup do post de introdução a orm em php vamos ver como ficou a classe gerada para o tabela City dentro da pasta “generated”, veja o arquivo BaseCity.php:
<?php /** * BaseCity * * This class has been auto-generated by the Doctrine ORM Framework * * @property integer $city_id * @property string $city * @property integer $country_id * @property timestamp $last_update * @property Country $Country * @property Doctrine_Collection $Address * * @package ##PACKAGE## * @subpackage ##SUBPACKAGE## * @author ##NAME## <##EMAIL##> * @version SVN: $Id: Builder.php 6401 2009-09-24 16:12:04Z guilhermeblanco $ */ abstract class BaseCity extends Doctrine_Record { public function setTableDefinition() { $this->setTableName('city'); $this->hasColumn('city_id', 'integer', 2, array( 'type' => 'integer', 'length' => 2, 'unsigned' => 1, 'primary' => true, 'autoincrement' => true, )); $this->hasColumn('city', 'string', 50, array( 'type' => 'string', 'length' => 50, 'fixed' => false, 'primary' => false, 'notnull' => true, 'autoincrement' => false, )); $this->hasColumn('country_id', 'integer', 2, array( 'type' => 'integer', 'length' => 2, 'unsigned' => 1, 'primary' => false, 'notnull' => true, 'autoincrement' => false, )); $this->hasColumn('last_update', 'timestamp', null, array( 'type' => 'timestamp', 'primary' => false, 'notnull' => true, 'autoincrement' => false, )); } public function setUp() { parent::setUp(); $this->hasOne('Country', array( 'local' => 'country_id', 'foreign' => 'country_id')); $this->hasMany('Address', array( 'local' => 'city_id', 'foreign' => 'city_id')); } }
Como pode ver os 2 métodos já falam por si quais são suas funções, note que o método setUp utiliza os métodos Doctrine_Record::hasMany e Doctrine_Record::hasOne. Esses são os métodos que você sempre usara para definir os relacionamentos das tabelas. Neste caso já foram gerados corretamente baseados nas foreign keys que estão no banco mas se quiser ainda pode modificar esses relacionamentos, para isso deve sobrescrever o método setUp na classe City.
Os métodos Doctrine_Record::hasMany e Doctrine_Record::hasOne tem a mesma definição recebendo 2 parâmetros $componentName e $options onde $componentName é o nome da tabela e $options um array associativo com as opções da relação. As 2 opções obrigatórias são “local” e “foreign”, ambas devem receber a respectiva coluna envolvida na relação das tabelas.
Depois que o relacionamento foi definido, você pode acessar os dados deste através de uma propriedade no modelo com o nome da tabela do relacionamento. Então digamos que você queira saber qual o nome do pais de uma cidade:
<?php require 'bootstrap.php'; // Busca a cidade pela chave primaria $cidade = Doctrine::getTable('City')->find(89); print 'Cidade: ' . $cidade->city . '<br />'; print 'Pais: ' . $cidade->Country->country;
A saída será a seguinte:
Cidade: Braslia
Pais: Brazil
Agora veja como ficou a classe BaseCountry:
<?php /** * BaseCountry * * This class has been auto-generated by the Doctrine ORM Framework * * @property integer $country_id * @property string $country * @property timestamp $last_update * @property Doctrine_Collection $City * * @package ##PACKAGE## * @subpackage ##SUBPACKAGE## * @author ##NAME## <##EMAIL##> * @version SVN: $Id: Builder.php 6401 2009-09-24 16:12:04Z guilhermeblanco $ */ abstract class BaseCountry extends Doctrine_Record { public function setTableDefinition() { $this->setTableName('country'); $this->hasColumn('country_id', 'integer', 2, array( 'type' => 'integer', 'length' => 2, 'unsigned' => 1, 'primary' => true, 'autoincrement' => true, )); $this->hasColumn('country', 'string', 50, array( 'type' => 'string', 'length' => 50, 'fixed' => false, 'primary' => false, 'notnull' => true, 'autoincrement' => false, )); $this->hasColumn('last_update', 'timestamp', null, array( 'type' => 'timestamp', 'primary' => false, 'notnull' => true, 'autoincrement' => false, )); } public function setUp() { parent::setUp(); $this->hasMany('City', array( 'local' => 'country_id', 'foreign' => 'country_id')); } }
Além de buscar os dados relacionados você pode gerenciar-los, para criar um novo pais e adicionar a ele algumas cidades você pode fazer o seguinte:
<?php require 'bootstrap.php'; $pais = new Country; $pais->country = 'Brasil'; $cidade = new City; $cidade->city = 'São Paulo'; $pais->City[] = $cidade; $cidade = new City; $cidade->city = 'Rio de Janeiro'; $pais->City[] = $cidade; $cidade = new City; $cidade->city = 'Curitiba'; $pais->City[] = $cidade; $pais->save(); print_r($pais->toArray());
A propriedade “City” é uma Doctrine_Collection referente a tabela “City” e como estamos criando uma novo registro, esta vazia. A saída do exemplo acima será essa:
Array
(
[country_id] => 110
[country] => Brasil
[last_update] =>
[City] => Array
(
[0] => Array
(
[city_id] => 601
[city] => São Paulo
[country_id] => 110
[last_update] =>
[Country] =>
)
[1] => Array
(
[city_id] => 602
[city] => Rio de Janeiro
[country_id] => 110
[last_update] =>
[Country] =>
)
[2] => Array
(
[city_id] => 603
[city] => Curitiba
[country_id] => 110
[last_update] =>
[Country] =>
)
)
)
Note que as cidades criadas já estão relacionadas com o pais. Se quiser listar todas as cidades cadastradas para um pais:
<?php require 'bootstrap.php'; $pais = Doctrine::getTable('Country')->find(15); print 'Pais: ' . $pais->country . '<br />'; // O método count retorna o número de registros em uma Doctrine_Collection print 'Total de cidades: ' . $pais->City->count() . '<br />'; foreach($pais->City as $c) print 'Cidade: ' . $c->city . '<br />';
O resultado será uma lista com esta:
Pais: Brazil
Total de cidades: 28
Cidade: Alvorada
Cidade: Angra dos Reis
...
O último exemplo funciona perfeitamente no entanto não é recomendável. O Doctrine faz um carregamento “preguiçoso” dos dados de entidades relacionadas sendo assim ele tem que fazer várias querys para que possa acessar estes dados, o ideal é utilizar a Doctrine Query Language.
Muito bom esse post também..
ajudou muito a entender como funcionam os relacionamentos.
Você vai fazer um turorial desse para relações n:n?
Obrigado!
Lucas, farei sim, pensei em adicionar a este post mas como pode ficar um pouco mais complicado deixarei para um post exclusivo, valew.
cara esse Doctrine pode mudar todo o conceito, excelente ferramenta!
e muito bons também os post-s muito bem explicados!
Cara muito bacana.
Michael,
Eu posso fazer todo um sistema usando somente o Doctrine para Banco de Dados?
Valew.
Pode sim! no post “Executanto operações CRUD com Doctrine” descrevi como realizar operações básicas, além destas você pode usar a DQL (doctrine query language) para consultas mais complicadas e até executar qualquer outra query via PDO.
Michael gostaria de saber como realizar relacionamentos com restrição onUpdate Cascade com Doctrine, estou trabalhando exatamente como você, gerando os modelos a partir da base de dados, mas segundo li na documentação só é suportado o onDelete Cascade o que posso fazer para resolver o problema?
Max, estou a um certo tempo sem trabalhar com doctrine mas pelo que me lembro é possivel trabalhar com ambos os métodos, dá uma olhada nesse link e neste. O primeiro é sobre a definição de relacionametos em geral e o segundo sobre cascata em nível de aplicação e de banco, o segundo tipo deve resolver.
ótimo, era essa a questão.
Mas aproveitando a deixa, vc nao está trabalhando com doctrine por que? encontrou uma ferramenta melhor?
vlw, abraço
Blz.
Estou sem usar por estar trabalhando mais em outros projetos no meu trabalho. Aqui temos um ORM próprio que já é usado a mais tempo e que todos conhecem.
Mas pretendo continuar usando doctrine e ainda recomendo, abraço.