Relacionamento entre Tabelas no Laravel

Relacionamento entre Tabelas no Laravel

Após um longo intervalo, estou retomando os artigos sobre Laravel. Vamos dar continuidade a aplicação que vinhamos desenvolvendo como exemplo e, neste artigo, falaremos especificamente sobre relacionamentos. Atualmente na nossa aplicação não temos nenhum relacionamento entre quem escreveu um artigo (usuarios) e os artigos. Neste contexto entram os relacionamentos e as chaves estrangeiras.

Assim como nos outros artigos vou começar com um referencial das funcionalidades do Laravel. Dentro dos nossos modelos, já temos funcionalidades disponíveis para definir os relacionamentos. Essas funcionalidades fazem parte do Eloquent.

Nossa aplicação foi atualizada para a versão 4.2 do Laravel, por isso essa é a versão que vou usar para explicar este artigo. A lógica em si e os métodos do Eloquent não vão mudar no Laravel 5, então não chega a ser um problema. Depois vou fazer um tutorial explicando como migrar para o Laravel 5.

Migrações

Vamos começar pelas migrações. Já escrevi um pouco sobre as migrações no artigo Banco de Dados no Laravel, agora vou falar especificamente sobre índices e chaves estrangeiras.

Índices

Definir índices nas migrações é bem simples. Você pode definir diretamente na criação da tabela, ou logo após criar ela (caso você queira um índice com mais campos será necessário fazer isso).

Aqui temos um exemplo de cada caso. Criamos uma tabela funcionarios com o campos cpf utilizando um índice único. Ou seja, este campos erá indexado e não vai permitir dois valores iguais. Depois de criar a tabela, adicionamos um outro índice simples, que usa os campos estado e cidade (normalmente seriam chaves estrangeira para outra tabela – fiz assim apenas pelo exemplo).

Outros métodos para índice são:

  • $table->primary(‘id’): definição de índice primário – lembrando que ao usar increments(‘id’) o índice primário será criado automaticamente;
  • $table->primary(array(‘primeiro’, ‘ultimo’)): cria um índice primário com dois campos;
  • $table->unique(‘cpf’): índice único no e-mail;
  • $table->index(‘estado’): índice simples no estado.

Cada índice criado é nomeado seguindo a seguinte regra: <tabela>_<campo>_<tipo do indice>. Ou seja, no caso do CPF nosso índice teria o seguinte nome funcionarios_cpf_unique. É importante saber o nome do índice, pois também existem métodos para apagar os índices criados e eles utilizam o nome para isso:

  • $table->dropPrimary(‘funcionarios_id_primary’);
  • $table->dropUnique(‘funcionarios_cpf_unique’);
  • $table->dropIndex(‘funcionarios_estado_index’).

Chaves Estrangeiras

Uma chave estrangeira é basicamente um índice associado a um valor de outra tabela. A maneira de criar uma chave estrangeira é bem simples:

Neste exemplo estamos criando uma tabela chamado cidades, com um campo estado_id relacionado a tabela estados. Esse é um exemplo clássico e simples de relacionamento 1 para N, onde cada cidade pertence a um estado e cada estado pode ter diversas cidades. A primeira coisa que precisamos fazer é definir o campo que vai armazenar a chave estrangeira:

Definimos um campo do tipo inteiro, sem sinal. Como não teremos IDs de estados menores que zero, não precisamos de sinal nesse campo. Além disso a capacidade do campo será maior, pois não teremos os negativos.

Se você quer ser capaz de cadastrar uma cidade sem o estado, deve criar o campo adicionado ->nullable() – isso permitirá que o campo seja NULL.

Logo depois definimos a chave estrangeira:

A primeira parte é a definição do campo que será a chave estrangeira (estado_id), depois informamos qual o campo será referenciado (id) e qual a tabela será referenciada (estados). A última parte é a definição do comportamento caso o registro relacionado seja removido – nesse caso definimos como cascade. O que vai acontecer neste caso é que, se possuirmos cidades com estado_id igual a 10 e o estado 10 for excluído, todas as cidades relacionadas serão excluídas automaticamente. Também é possível definir o método onUpdate() da mesma forma.

Para remover uma chave estrangeira você deve usar o comando: $table->dropForeign(‘cidades_estado_id_foreign’);. Mesma lógica dos outros índices.

Mas esse é um exemplo bem simples. Um caso mais complexo é o das tabelas relacionamento. Por exemplo, quando precisamos relacionar pessoas e as cidades onde já moraram, uma cidade pode estar relacionada a várias pessoas e uma pessoa pode estar relacionada a várias cidades. Neste caso temos um relacionamento N para N. O código necessário seria assim:

Nesse caso temos as duas tabelas pessoas e cidades, além de uma terceira tabela relacionamento cidade_pessoa. A convenção do Laravel é sempre colocar o nome das tabelas no singular e em ordem alfabética. Não é obrigatório, mas facilita a implementação dos modelos depois. Na tabela relacionamento temos uma chave estrangeira para cada tabela, assim podemos criar o relacionamento N para N.

Eloquent

Agora vamos ver como ficam os relacionamentos dentro dos nossos modelos Eloquent. É bem fácil e simplifica o desenvolvimento. As vezes não é o ideal com relação a performance. Primeiro vamos ver o código necessário para cada tipo de relacionamento:

Além dessas opções, ainda é possível definir relacionamento com uma tabela intermediária (HasManyThrough) e relacionamentos polimorfos (Polymorphic Relations), mas não vou explicar esses relacionamentos em detalhe. Se você que saber mais consulte a documentação. Todos os métodos do código acima permitem definir quais os campos e tabelas você usou:

  • $this->hasOne(‘Conta’, ‘usuario_id’, ‘id’): por padrão o Laravel vai usar o nome da tabela mais ‘_id’ como chave estrangeira e vai usar ‘id’ como chave local. Se você usou campos diferentes precisará preencher esses dois parâmetros adicionais;
  • $this->belongsTo(‘Usuario’, ‘usuario_id’, ‘id’): o método belongsTo segue a mesma lógica;
  • $this->hasMany(‘Cidade’, ‘usuario_id’, ‘id’): também segue o mesmo padrão, nenhum mistério.
  • $this->belongsToMany(‘Cidade, ‘pessoa_cidade’, ‘pessoa_id’, ‘cidade_id’): como neste caso temos uma tabela intermediária, você pode usar o segundo parâmetro para definir o nome dessa tabela. O Laravel vai sempre procurar pela tabela com nomes no singular e em ordem alfabética – caso você use algo diferente, precisa especificar aqui. Os dois últimos parâmetros são as chaves estrangeiras da tabela relacionamento.

Buscando Registros

Agora que você já sabe como implementar as migrações e os models, vamos ver como se usam os relacionamentos. Os métodos podem ser acessados tanto como propriedades dos modelos ou como métodos para adicionar condições:

Você também pode incrementar o método no model para incluir condições. Isso permite deixar seu controller bem limpo adicionando as buscas no model:

Performance

Apesar de muito simples e muito claro de implementar, essa facilidade pode fazer com que você cause problemas de performance na sua aplicação. Veja o seguinte caso e tente pensar qual é o problema:

Conseguiu descobrir? O problema deste código é que o Laravel vai fazer uma busca no banco para pegar a lista de cidades e outra busca para cada cidade para buscar as informações do estado. Se você tiver 50 cidades, serão executadas 51 buscas no banco, reduzindo a performance.

Uma maneira de resolver isso é bem simples. Quando buscar as cidades, é possível buscar os estados previamente, isso é chamado de Eager Loading. O código é quase igual, a diferença é o método with:

Esse código vai executar apenas duas buscas no banco:

Na segunda query serão usadas todas as chaves dos estados necessárias. Essa pequena mudança vai trazer um grande ganho de performance para sua aplicação.

Aplicação

Agora que já temos a referência para os relacionamentos, vamos adicionar um relacionamento na nossa aplicação. Se você ainda não conhecer nossa aplicação veja o repositório no GitHub. Para baixar o código que vamos usar aqui faça o download da versão 1.5.2.

Migração

O que queremos fazer é que o artigo esteja relacionado a um autor – que no caso será um usuário do sistema. Primeiro vamos criar uma migração executando o comando php artisan migrate:make relacionamento_artigo_usuario. Vamos colocar o seguinte conteúdo:

Este código é diferente dos códigos que expliquei antes. Consegue saber o por quê? No meu banco de dados (talvez no seu também) já existem artigos criados. Para poder criar a chave estrangeira temos duas opções:

  • preencher o novo campo usuario_id com algum valor para associar a algum usuário – foi o que fiz aqui.
  • criar o campo usuario_id com nullable(). Assim seria possível ter o valor nulo e criar a chave não seria um problema. Porém teríamos artigos sem associação a usuários.

Agora execute a migração e vamos para os models

Models

Temos dois models, Usuario e Artigo. Adicione o seguinte método no model Usuario:

E adicione o seguinte método no model Artigo:

Controller

Agora abra o arquivo app/controllers/ArtigosController.php e altere o método getIndex() para adicionarmos o with e garantirmos uma boa performance:

E altere também o método postInserir() para incluir o usuário que criou o artigo.

View

Para finalizar vamos modificar a lista de artigos para incluir o nome do autor. Dentro do foreach ($artigos as $artigo) coloque o seguinte código ( a diferença está no usuario->nome):

É isso. Estamos com nossa aplicação atualizada. Ao acessar a lista de artigos em <dominio>/artigos você verá o nome do autor:

Autor do Artigo

Página Inicial

Além dessas alterações, também coloquei os artigos na página inicial. Uma alteração bem simples, por isso não vou explicar aqui. Veja as modificações no repositório.

Conclusão

Neste artigo expliquei um pouco mais sobre como utilizar banco de dados no Laravel. Infelizmente tenho tido dificuldade em encontrar tempo para escrever, então vou tentar quebrar em mais artigos as próximas partes. Minha próxima tarefa é migrar essa aplicação para o Laravel 5. Se você tiver alguma dúvida, deixe nos comentários que eu respondo assim que possível.

Este post é parte da série Desenvolvendo com Laravel.

4 respostas para “Relacionamento entre Tabelas no Laravel”

  1. OLá Oscar , tenho uma dúvida , fiz um relacionamento entre as tabelas e estou tendo dificuldade para o campo estar sendo preenchido como nulo . ele é setado como nulo como padrão, agora se você preenche o campo , e depois deseja alterar , não consegue da erro no mysql . veja o código –

    UP –
    $table->integer(‘gallery_id’)->unsigned()->nullable()->default(null);
    $table->foreign(‘gallery_id’)->references(‘id’)->on(‘photo_gallery’);

    DOWN –
    $table->dropForeign(‘posts_gallery_id_foreign’);

    vcê saberia dizer o que é ? vc so consegue preencher o campo como nulo no início , agora se você colocar algum valor no relacioanmento e depois for em UPDATE e quiser colocar nulo de novo, não consegue .

    1. Blz Murilo?
      A migration parece estar OK. Você pode inclusive remover o “->default(null)”. Quando você seta como nullable e não atribui nada no campo ele já é criado como NULL.
      O problema deve ser a atribuição. Como você está fazendo? Deveria ser algo assim:

      $post = Post::find($id);
      $post->gallery_id = NULL;
      $post->save();

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *