ORM Eloquent: Model
Tags: laravel, beginners
Published: 2022-10-19 00:14:15

### ORM - Object Relational Mapping

ORM é uma técnica que busca aproximar o paradigma da programação orientada a objetos ao paradigma dos bancos de dados relacionais.

E o ORM pode ser adotado de duas formas:
* database first
* code first

> Database first

Esta abordagem é mais usual em sistemas legados, quando o _schema_ do banco de dados está definido e bastante evoluído.

Porém, é preciso um cuidado maior ao analisar os relacionamentos entre as tabelas e como resolver dentro do ORM. Um estudo pouco aprofundado pode resultar em perda da performance da aplicação, levando-se à falsa impressão que o problema está no ORM quando na verdade a estratégia adotada não foi a mais apropriada.

---

> Code First

Nesta abordagem as entidades são criadas na aplicação para depois serem criadas no banco de dados, através de um processo chamado de  **migração**, cujo objetivo é guardar o histórico de alterações feitas no _schema_. Este processo é análogo ao versionamento de código feito, por exemplo, no [GitHub](https://github.com/).

Entretanto, há situações onde este cenário não é aplicável. Para casos como este, o ORM pode gerar scripts DDL que serão executados no banco de dados, seja por meio de uma sessão aberta no terminal ou através de uma aplicação desenvolvida especificamente para essa finalidade.

---

### Eloquent

O Eloquent é o ORM oficial em aplicações Laravel.

Entretanto a utilização do Eloquent não é obrigatória, sendo possível desenvolver uma aplicação completa, com acesso a bancos de dados utilizando apenas a classe **Illuminate\Support\Facades\DB**.

Assim como outros ORMs, o Eloquent traz uma classe (abstrata) para representar as entidades do banco de dados. Trata-se do **Illuminate\Database\Eloquent\Model**. 

Ao longo deste artigo veremos algumas das características da classe `model` e como resolver problemas que podem aparecer durante o processo de mapeamento de um _schema_ de banco de dados.

:warning: Apesar deste ser um artigo com cunho técnico, não criaremos projetos ou aplicações e também não haverá interação com bancos de dados.

Todas as informações contidas neste artigo podem ser encontradas na documentação oficial do framework Laravel neste [link](https://laravel.com/docs/9.x/eloquent).

---

### Model

O modelo, dentro da arquitetura MVC, é representado aqui pela letra 'M'. O modelo é o objeto da aplicação que mais se aproxima do _schema_ do banco de dados, sendo usado com frequência para persistência de informações. 

Em outras aplicações, o modelo exerce uma função diferente, realizando o transporte de dados entre as camadas de apresentação e negócio. 

Neste caso, cria-se uma entidade para fazer a persistência. Este novo objeto (entidade) não se comunica diretamente com o modelo, embora existam projetos com essa característica.

---

Em aplicações Laravel a maneira mais fácil de criar um modelo é através do Artisan, que pode ser executado a partir do terminal no diretório raiz da aplicação, com o seguinte comando:

`php artisan make:model <nome_do_modelo>`

Assim que o comando for concluído, obteremos um novo arquivo que estenderá de **Illuminate\Database\Eloquent\Model** e será salvo no diretório \App\Models\.

O código gerado pelo comando será semelhante a este:

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Job extends Model
{
    use HasFactory;
}

```

Note que a criação do modelo adicionou a trait `HasFactory`, responsável por 'conectar' esta modelo com uma classe `factory`, que não será abordada neste artigo.

---

## Tabela <a name="protected_table"></a>

Como dito anteriormente, o modelo representará uma tabela do banco de dados da nossa aplicação. Por convenção, o Laravel assumirá que o nome da tabela representada pelo modelo `Job` será `Jobs`. Ou seja, o nome da classe no plural.

Porém, haverá casos onde o nome da tabela não se encaixará nessa convenção. A solução para situações como esta é informar o nome da tabela explicitamente. Isto é feito adicionando o atributo privado `$table` à nossa classe. 

O exemplo a seguir ilustra como implementar esta modificação.

```php
protected $table = 'company_jobs';
```

---

## Chave primária <a name="primary_key"></a>

Os nomes de chaves primárias são definidos pelo banco de dados. A exceção ocorre quando, no script DDL, o nome da chave primária é atribuído manualmente.

A exemplo do que fizemos para identificar explicitamente o nome da tabela referenciada pelo modelo, vamos 'informar' ao modelo qual o nome da chave primária, através do atributo protegido `$primaryKey`.

E podemos ver no trecho de código logo abaixo, como deve ficar a implementação dentro do nosso modelo:

```php
protected $primaryKey = 'job_id';
```

:warning: O Eloquent ORM não suporta chaves primárias compostas nativamente. Entretanto, uma pesquisa na internet trará diversas soluções aplicáveis a vários cenários.

---

## Chave estrangeira <a name="foreign_key"></a>

Chaves estrangeiras tem como objetivo relacionar tabelas evitando a replicação de todas as informações de uma tabela em outras. 

Para facilitar a compreensão do conceito de chave estrangeira, vamos imaginar o cliente de uma instituição financeira, cujo cadastro contém informações sobre endereço, fonte de renda e filiação, por exemplo.

Para este cliente, sempre que ele realizar alguma operação financeira, serão gravadas somente informações relativas à operação, como data, valor, número do documento (se houver), tipo da operação (crédito ou débito) e o ID do cliente.

Ao informamos o ID do cliente na tabela de operações financeiras estamos evitando que todas as informações relativas ao cadastro sejam replicadas em todas as operações, o que aumentaria significativamente o tamanho de cada registro e, por consequência, prejudicar a performance da aplicação.

A configuração de chave estrangeira no modelo pode ser feita através da criação de um método público que retornará o modelo que possui a chave primária.

Um exemplo desse método pode ser visto logo abaixo.

```php
public function user()
{
    return $this->belongsTo(User::class);
}
```

O método `belongsTo` recebe como primeiro argumento o modelo que representa a 'tabela pai' do relacionamento e, através de métodos auxiliares, identifica a coluna que corresponde à chave primária da tabela pai. 

Este cenário pode ser conveniente, quando a abordagem adotada na aplicação tenha sido **code first**. Na eventualidade de uma abordagem diferente, como **database first**, convém não delegar ao framework, a tarefa de encontrar os relacionamentos automaticamente.

Para resolver essa questão, o método `belongsTo` pode receber outros argumentos, como nome da chave estrangeira (`$foreignKey`), nome da chave primária da tabela pai (`$ownerKey`) e nome da **constraint** no banco de dados (`$relation`).

Abaixo a assinatura do método `belongsTo`.

```php
/**
 * Define an inverse one-to-one or many relationship.
 *
 * @param  string  $related
 * @param  string|null  $foreignKey
 * @param  string|null  $ownerKey
 * @param  string|null  $relation
 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
 */
public function belongsTo($related, $foreignKey = null, $ownerKey = null, $relation = null)

```
---

## Relacionamento 'um pra um' <a name="one_to_one"></a>

Este tipo de relacionamento entre entidades indica que, para cada instância do modelo 'A' pode existir uma ou nenhuma instância do modelo 'B'. Para citar um exemplo real, uma pessoa física tem apenas um CPF.

A forma de implementar este relacionamento no modelo 'A' é através de um método que retornará a instância do modelo 'B' associado.

Abaixo um exemplo da implementação deste relacionamento.

```php
public function cpf()
{
     return $this->hasOne(Cpf::class);    
}

```

O método `hasOne` recebe como primeiro argumento o modelo associado. O framework então, cria uma instância do modelo informado para inferir informações sobre o relacionamento entre os modelos.

E para reduzir o 'trabalho' feito pelo framework, o método `hasOne` pode receber o nome da chave estrangeira (`$foreignKey`) e o nome da chave primária (`$localKey`) do modelo associado.

Abaixo temos a assinatura do método `hasOne`.

```php
/**
 * Define a one-to-one relationship.
 *
 * @param  string  $related
 * @param  string|null  $foreignKey
 * @param  string|null  $localKey
 * @return \Illuminate\Database\Eloquent\Relations\HasOne
 */
public function hasOne($related, $foreignKey = null, $localKey = null)
```

---

## Relacionamento 'um pra muitos' <a name="one_to_many"></a>

Este é o relacionamento mais comum que vemos na maioria (senão em todas...rs) das aplicações. Utilizando um exemplo do mundo real, significa dizer que uma pessoa pode ter um ou mais filhos, ou nenhum filho.

Este tipo de relacionamento entre modelos também é resolvido através de um método, que ao ser chamado, retornará uma ou mais instâncias do modelo associado. 

Abaixo temos um exemplo da implementação deste método em um modelo.

```php
public function kid()
{
	return $this->hasMany(Kid::class);
}
```

O método `hasMany` receberá como argumento a classe do modelo 'B', associado ao modelo 'A'.

Logo abaixo, podemos notar que a assinatura do método `hasMany` é semelhante à assinatura do método `hasOne`, visto anteriormente.

```php
/**
 * Define a one-to-many relationship.
 *
 * @param  string  $related
 * @param  string|null  $foreignKey
 * @param  string|null  $localKey
 * @return \Illuminate\Database\Eloquent\Relations\HasMany
 */
public function hasMany($related, $foreignKey = null, $localKey = null)
```

---

## Ocultar propriedades <a name="hidden"></a>

Como dito anteriormente, há cenários onde o modelo tem um papel diferente interagindo com a camada de apresentação da aplicação e se comunicando somente com a camada de negócio. 

Persistir os dados, nestes casos, ficará a cargo de outro objeto conhecido como entidade. Assim como o modelo, a entidade se comunica somente com a camada de negócio. 

Esta abordagem traz isolamento entre as camadas de apresentação e persistência.

Mas existem aplicações onde o modelo é utilizado para fazer a persistência dos dados e mostrar informações na camada de apresentação.

Quando essa situação ocorre, convém ocultar algumas propriedades do modelo para não serem mostradas na camada de apresentação.

E fazemos isso através do atributo protegido $hidden, que recebe um array das propriedades que não devem ser mostradas.

Abaixo um exemplo da declaração deste atributo.

```php
protected $hidden = [
	'shop_id',
	'created_at',
	'updated_at',
];

```
---

## Inserção em massa <a name="fillable"></a>

Inserção em massa é uma técnica que visa a redução de código, sendo mais comum em aplicações web.

Nesta técnica, ao invés atribuir os valores explicitamente para cada propriedade do modelo, indicamos ao modelo quais colunas poderão receber os valores diretamente da camada de negócio.

A utilização desta técnica reduz significativamente a manutenção com código. Outro benefício é evitar que um registro seja persistido através da técnica de 'sql injection'. 

Para implementar a inserção em massa no modelo, declaramos o atributo protegido `$fillable`. Esse atributo receberá um array com os campos que poderão ser tratados durante o processo de inserção em massa.

A seguir um exemplo da implementação deste atributo em um modelo.

```php
protected $fillable = [
	'description',
	'price',
];
```
---

## Created_at e Updated_at<a name="timestamps"></a>

Quando a migração é criada, temos a possibilidade de adicionar duas colunas na tabela representada pelo modelo: created_at e update_at. 

As duas colunas serão do tipo 'datetime' e o objetivo delas é armazenar o momento que o registro foi criado e a última vez que o registro foi atualizado.

Embora sejam informações úteis, elas nem sempre serão necessárias no modelo. E para evitar que o Eloquent faça esse controle basta adicionar o atributo público `$timestamps`, que receberá um valor booleano. 

O valor padrão deste atributo é `true` e informamos `false` para desabilitar esse controle.

Abaixo um exemplo da implementação desse atributo.

```php
public $timestamps = false;
```

---

### Conclusão

Neste artigo busquei trazer algumas das características do modelo do Eloquent ORM propondo soluções aos problemas mais comuns.

Tudo o que foi mostrado está na documentação oficial do Eloquent.

Mas não fique preso apenas à documentação pois ela pode não apresentar solução para problemas mais específicos. 

Até breve.

:sunglasses: