PHP

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

8 min de leitura

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

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

<h2>Introdução ao Eloquent ORM</h2>

<p>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.</p>

<h2>Models: Fundação do Eloquent</h2>

<h3>Criando seu primeiro Model</h3>

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

<pre><code class="language-bash">php artisan make:model Post</code></pre>

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

<pre><code class="language-php">&lt;?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model

{

protected $table = &#039;meus_posts&#039;; // nome customizado

protected $fillable = [&#039;title&#039;, &#039;content&#039;, &#039;author_id&#039;];

}</code></pre>

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

<pre><code class="language-php">protected $hidden = [&#039;password&#039;, &#039;remember_token&#039;];

protected $visible = [&#039;id&#039;, &#039;title&#039;, &#039;content&#039;];</code></pre>

<h3>Operações CRUD básicas</h3>

<p>Com o model definido, as operações são intuitivas:</p>

<pre><code class="language-php">// CREATE

$post = Post::create([

&#039;title&#039; =&gt; &#039;Meu primeiro post&#039;,

&#039;content&#039; =&gt; &#039;Conteúdo aqui&#039;,

&#039;author_id&#039; =&gt; 1

]);

// READ

$post = Post::find(1);

$posts = Post::all();

$posts = Post::where(&#039;author_id&#039;, 1)-&gt;get();

// UPDATE

$post-&gt;update([&#039;title&#039; =&gt; &#039;Título atualizado&#039;]);

// DELETE

$post-&gt;delete();

Post::destroy([1, 2, 3]); // deleta múltiplos</code></pre>

<h2>Relacionamentos: Conectando Models</h2>

<h3>Um para Muitos (One to Many)</h3>

<p>O relacionamento mais comum. Um autor tem muitos posts. Defina no model <code>Author</code>:</p>

<pre><code class="language-php">&lt;?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-&gt;hasMany(Post::class);

}

}</code></pre>

<p>E no model <code>Post</code>, defina o inverso:</p>

<pre><code class="language-php">&lt;?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-&gt;belongsTo(Author::class);

}

}</code></pre>

<p>Agora você usa os relacionamentos assim:</p>

<pre><code class="language-php">$author = Author::find(1);

$posts = $author-&gt;posts; // carrega todos os posts do autor

$post = Post::find(1);

$author = $post-&gt;author; // carrega o autor do post</code></pre>

<h3>Muitos para Muitos (Many to Many)</h3>

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

<pre><code class="language-php">Schema::create(&#039;category_post&#039;, function (Blueprint $table) {

$table-&gt;id();

$table-&gt;foreignId(&#039;post_id&#039;)-&gt;constrained()-&gt;cascadeOnDelete();

$table-&gt;foreignId(&#039;category_id&#039;)-&gt;constrained()-&gt;cascadeOnDelete();

$table-&gt;timestamps();

});</code></pre>

<p>Defina no model <code>Post</code>:</p>

<pre><code class="language-php">&lt;?php

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Post extends Model

{

public function categories(): BelongsToMany

{

return $this-&gt;belongsToMany(Category::class);

}

}</code></pre>

<p>E no model <code>Category</code>:</p>

<pre><code class="language-php">public function posts(): BelongsToMany

{

return $this-&gt;belongsToMany(Post::class);

}</code></pre>

<p>Use assim:</p>

<pre><code class="language-php">$post = Post::find(1);

$post-&gt;categories; // todas as categorias do post

$post-&gt;categories()-&gt;attach(2); // associa categoria id 2

$post-&gt;categories()-&gt;detach(2); // desassocia

$post-&gt;categories()-&gt;sync([1, 3]); // sincroniza (remove outras)</code></pre>

<h2>Scopes: Consultas Reutilizáveis</h2>

<h3>Local Scopes</h3>

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

<pre><code class="language-php">&lt;?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;

class Post extends Model

{

public function scopePublished(Builder $query): Builder

{

return $query-&gt;where(&#039;published&#039;, true);

}

public function scopeByAuthor(Builder $query, int $authorId): Builder

{

return $query-&gt;where(&#039;author_id&#039;, $authorId);

}

public function scopeRecent(Builder $query): Builder

{

return $query-&gt;orderBy(&#039;created_at&#039;, &#039;desc&#039;);

}

}</code></pre>

<p>Use os scopes como métodos sem o prefixo <code>scope</code>:</p>

<pre><code class="language-php">// Cada scope encadeia automaticamente

$posts = Post::published()

-&gt;byAuthor(1)

-&gt;recent()

-&gt;get();

// Equivalente a:

// SELECT * FROM posts WHERE published = 1 AND author_id = 1

// ORDER BY created_at DESC</code></pre>

<h3>Global Scopes</h3>

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

<pre><code class="language-php">&lt;?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-&gt;where(&#039;published&#039;, true);

}

}</code></pre>

<p>Registre no model:</p>

<pre><code class="language-php">protected static function booted(): void

{

static::addGlobalScope(new PublishedScope());

}</code></pre>

<p>Agora <code>Post::all()</code> retorna apenas posts publicados automaticamente. Para ignorar: <code>Post::withoutGlobalScopes()-&gt;get()</code>.</p>

<h2>Conclusão</h2>

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

<h2>Referências</h2>

<ul>

<li><a href="https://laravel.com/docs/eloquent" target="_blank" rel="noopener noreferrer">Documentação oficial Eloquent ORM</a></li>

<li><a href="https://laravel.com/docs/eloquent-relationships" target="_blank" rel="noopener noreferrer">Eloquent Relationships - Laravel Docs</a></li>

<li><a href="https://laravel.com/docs/eloquent#query-scopes" target="_blank" rel="noopener noreferrer">Query Scopes - Laravel Docs</a></li>

<li><a href="https://laravel.com/docs/eloquent#retrieving-single-models" target="_blank" rel="noopener noreferrer">Laravel Best Practices - Eloquent Patterns</a></li>

<li><a href="https://www.oreilly.com/library/view/laravel-up-and/9781491926421/" target="_blank" rel="noopener noreferrer">Livro: &quot;Laravel Up and Running&quot; - Matt Stauffer</a></li>

</ul>

Comentários

Mais em PHP

Dominando Sessões e Cookies: Autenticação Stateful em PHP em Projetos Reais
Dominando Sessões e Cookies: Autenticação Stateful em PHP em Projetos Reais

Entendendo Sessões e Cookies em PHP Sessões e cookies são os pilares da auten...

O que Todo Dev Deve Saber sobre Strings em PHP: Manipulação, Funções e Formatação
O que Todo Dev Deve Saber sobre Strings em PHP: Manipulação, Funções e Formatação

Fundamentos de Strings em PHP Uma string em PHP é uma sequência de caracteres...

Guia Completo de Testes de Integração e Feature Tests com Laravel
Guia Completo de Testes de Integração e Feature Tests com Laravel

Entendendo Testes de Integração em Laravel Testes de integração verificam com...