Boas Práticas de Eloquent ORM: Models, Relacionamentos e Scopes para Times Ágeis Já leu

Introdução ao Eloquent ORM O Eloquent é o Object-Relational Mapping (ORM) padrão do Laravel, permitindo interagir com bancos de dados usando orientação a objetos. Em vez de escrever SQL puro, você trabalha com modelos PHP que representam suas tabelas. Esta abordagem torna o código mais legível, seguro contra SQL injection e muito mais produtivo. Nesta aula, você aprenderá os pilares fundamentais: criar modelos, definir relacionamentos e implementar scopes para consultas reutilizáveis. Models: Fundação do Eloquent Criando seu primeiro Model Um Model no Eloquent é uma classe PHP que estende e representa uma tabela do banco de dados. Para criar um model, use o artisan: Isto gera um arquivo . O Laravel assume que a tabela correspondente é (plural, em snakecase). Se sua tabela tiver outro nome, defina a propriedade : Relacionamentos: Conectando Models Um para Muitos (One to Many) O relacionamento mais comum. Um autor tem muitos posts. Defina no model : E no model , defina o inverso: Agora

Introdução ao Eloquent ORM

O Eloquent é o Object-Relational Mapping (ORM) padrão do Laravel, permitindo interagir com bancos de dados usando orientação a objetos. Em vez de escrever SQL puro, você trabalha com modelos PHP que representam suas tabelas. Esta abordagem torna o código mais legível, seguro contra SQL injection e muito mais produtivo. Nesta aula, você aprenderá os pilares fundamentais: criar modelos, definir relacionamentos e implementar scopes para consultas reutilizáveis.

Models: Fundação do Eloquent

Criando seu primeiro Model

Um Model no Eloquent é uma classe PHP que estende Model e representa uma tabela do banco de dados. Para criar um model, use o artisan:

php artisan make:model Post

Isto gera um arquivo app/Models/Post.php. O Laravel assume que a tabela correspondente é posts (plural, em snake_case). Se sua tabela tiver outro nome, defina a propriedade $table:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $table = 'meus_posts'; // nome customizado

    protected $fillable = ['title', 'content', 'author_id'];
}

A propriedade $fillable define quais atributos podem ser atribuídos em massa usando create() ou update(), protegendo contra atribuição em massa acidental. Use $visible e $hidden para controlar quais atributos aparecem em JSON:

protected $hidden = ['password', 'remember_token'];
protected $visible = ['id', 'title', 'content'];

Operações CRUD básicas

Com o model definido, as operações são intuitivas:

// CREATE
$post = Post::create([
    'title' => 'Meu primeiro post',
    'content' => 'Conteúdo aqui',
    'author_id' => 1
]);

// READ
$post = Post::find(1);
$posts = Post::all();
$posts = Post::where('author_id', 1)->get();

// UPDATE
$post->update(['title' => 'Título atualizado']);

// DELETE
$post->delete();
Post::destroy([1, 2, 3]); // deleta múltiplos

Relacionamentos: Conectando Models

Um para Muitos (One to Many)

O relacionamento mais comum. Um autor tem muitos posts. Defina no model Author:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Author extends Model
{
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}

E no model Post, defina o inverso:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Post extends Model
{
    public function author(): BelongsTo
    {
        return $this->belongsTo(Author::class);
    }
}

Agora você usa os relacionamentos assim:

$author = Author::find(1);
$posts = $author->posts; // carrega todos os posts do autor

$post = Post::find(1);
$author = $post->author; // carrega o autor do post

Muitos para Muitos (Many to Many)

Um post pertence a várias categorias e uma categoria tem vários posts. Crie uma tabela pivot category_post:

Schema::create('category_post', function (Blueprint $table) {
    $table->id();
    $table->foreignId('post_id')->constrained()->cascadeOnDelete();
    $table->foreignId('category_id')->constrained()->cascadeOnDelete();
    $table->timestamps();
});

Defina no model Post:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Post extends Model
{
    public function categories(): BelongsToMany
    {
        return $this->belongsToMany(Category::class);
    }
}

E no model Category:

public function posts(): BelongsToMany
{
    return $this->belongsToMany(Post::class);
}

Use assim:

$post = Post::find(1);
$post->categories; // todas as categorias do post

$post->categories()->attach(2); // associa categoria id 2
$post->categories()->detach(2); // desassocia
$post->categories()->sync([1, 3]); // sincroniza (remove outras)

Scopes: Consultas Reutilizáveis

Local Scopes

Scopes permitem encapsular lógica de filtro em métodos reutilizáveis. No model Post, crie um scope prefixado com scope:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;

class Post extends Model
{
    public function scopePublished(Builder $query): Builder
    {
        return $query->where('published', true);
    }

    public function scopeByAuthor(Builder $query, int $authorId): Builder
    {
        return $query->where('author_id', $authorId);
    }

    public function scopeRecent(Builder $query): Builder
    {
        return $query->orderBy('created_at', 'desc');
    }
}

Use os scopes como métodos sem o prefixo scope:

// Cada scope encadeia automaticamente
$posts = Post::published()
    ->byAuthor(1)
    ->recent()
    ->get();

// Equivalente a:
// SELECT * FROM posts WHERE published = 1 AND author_id = 1 
// ORDER BY created_at DESC

Global Scopes

Aplique filtros automaticamente a todas as consultas de um model. Útil para soft deletes ou multi-tenant:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Scope;

class PublishedScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        $builder->where('published', true);
    }
}

Registre no model:

protected static function booted(): void
{
    static::addGlobalScope(new PublishedScope());
}

Agora Post::all() retorna apenas posts publicados automaticamente. Para ignorar: Post::withoutGlobalScopes()->get().

Conclusão

Você aprendeu os três pilares do Eloquent: Models abstraem suas tabelas em classes PHP com operações intuitivas; Relacionamentos conectam models de forma elegante (one-to-many, many-to-many); Scopes reutilizam lógica de filtro, mantendo controllers limpos. Domine estes conceitos e seu código Laravel será significativamente mais organizado e eficiente.

Referências


Artigos relacionados