aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Webontwikkeling

Laravel Eloquent-relaties: gids voor hasMany en belongsTo

Met Laravel Eloquent-relaties druk je de verbanden tussen je databasetabellen uit via leesbare, elegante PHP-objecten. De reacties op een blogartikel, de bestellingen van een gebruiker, de producten in een categorie… in plaats van met de hand JOIN-query's te schrijven om deze koppelingen te beheren, definieer je ze met één enkele methode op je Eloquent-modellen. In dit artikel behandelen we de twee meest voorkomende relaties — hasMany en belongsTo — met voorbeelden, en gaan we daarna in op de andere relatietypes en op eager loading, de ruggengraat van relationele prestaties.

Overzicht van relatietypes

Eloquent laat je gerelateerde modellen als methoden declareren. In een doorsnee applicatie kom je deze relaties tegen:

  • Een-op-een (hasOne / belongsTo): een gebruiker heeft één profiel.
  • Een-op-veel (hasMany / belongsTo): een artikel heeft veel reacties.
  • Veel-op-veel (belongsToMany): een artikel heeft veel labels, en een label hoort bij veel artikelen.
  • Verre en polymorfe relaties: geavanceerdere scenario's zoals hasManyThrough en morphMany.

Dit artikel richt zich op de hoeksteen, de een-op-veel-relatie, want hasMany en zijn tegenhanger belongsTo werken samen.

hasMany: "één record bezit vele records"

Het klassieke voorbeeld is een blog: een Post bevat veel Comment-records. Aan de databasekant heeft de tabel comments een kolom post_id. Deze foreign key vertelt welke reactie bij welk artikel hoort.

// app/Models/Post.php
class Post extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

Standaard gaat Eloquent ervan uit dat de foreign key de naam van het bovenliggende model (enkelvoud) + _id is, dus post_id. Heeft je kolom een andere naam, dan kun je dat expliciet opgeven met het tweede en derde argument:

return $this->hasMany(Comment::class, 'post_id', 'id');

Je kunt de reacties van een artikel nu benaderen alsof het een eigenschap is:

$post = Post::find(1);

foreach ($post->comments as $comment) {
    echo $comment->body;
}

$post->comments zonder haakjes geeft een Collection terug, terwijl $post->comments() een query builder teruggeeft. Met die laatste kun je de relatie verfijnen:

$approved = $post->comments()
    ->where('approved', true)
    ->latest()
    ->get();

belongsTo: het andere uiteinde van de relatie

belongsTo is de omgekeerde van hasMany. Denk vanuit het oogpunt van de reactie: elke reactie hoort bij een artikel. De kant die de foreign key bevat — de tabel comments met zijn kolom post_id — is altijd de belongsTo-kant.

// app/Models/Comment.php
class Comment extends Model
{
    public function post()
    {
        return $this->belongsTo(Post::class);
    }
}

Zo loop je eenvoudig van een reactie terug naar het bovenliggende artikel:

$comment = Comment::find(5);
echo $comment->post->title;

Beide kanten definiëren is een goede gewoonte: hasMany op de Post en belongsTo op de Comment. Zo kun je de relatie in beide richtingen doorlopen en maak je je bedoeling duidelijk voor iedereen die de code leest.

Records aanmaken via een relatie

Eloquent-relaties zijn niet alleen om te lezen; je kunt nieuwe records aanmaken met de foreign key automatisch ingevuld. De methoden save en create stellen post_id voor je in:

$post = Post::find(1);

$post->comments()->create([
    'body'   => 'Geweldig artikel, bedankt!',
    'author' => 'Sanne',
]);

Om create te kunnen gebruiken, vergeet niet de betreffende velden toe te voegen aan de $fillable-array van het model. In de andere richting gebruik je associate om een bestaande reactie aan een artikel te koppelen:

$comment->post()->associate($post);
$comment->save();

Het N+1-probleem en eager loading

De meest gemaakte fout met relaties is een verborgen prestatieval: het N+1-queryprobleem. Stel je voor dat je in een lus de auteur van elk artikel opvraagt:

$posts = Post::all(); // 1 query

foreach ($posts as $post) {
    echo $post->author->name; // 1 extra query per artikel
}

Met 50 artikelen betekent dat 1 + 50 = 51 query's. De oplossing is eager loading: met de methode with laad je de relatie vooraf in één extra query.

$posts = Post::with('author')->get(); // 2 query's in totaal

foreach ($posts as $post) {
    echo $post->author->name; // geen extra query
}

Achter de schermen voert Eloquent een WHERE ... IN (...)-query uit om alle auteurs in één keer op te halen. Je kunt ook geneste en meerdere relaties eager loaden:

$posts = Post::with(['author', 'comments.user'])->get();

Om deze fout vroeg in de ontwikkeling op te sporen, kun je lazy loading verbieden met Model::preventLazyLoading(), of het aantal query's volgen met een tool als Laravel Debugbar. Heb je alleen het aantal gerelateerde records nodig, gebruik dan withCount in plaats van alle rijen in het geheugen te laden:

$posts = Post::withCount('comments')->get();
echo $posts->first()->comments_count;

Praktische tips

  • Voeg altijd een database-index toe op foreign-keykolommen; dat versnelt relatiequery's merkbaar.
  • Gebruik in migrations foreignId('post_id')->constrained()->cascadeOnDelete() om zowel de index als een integriteitsbeperking aan te maken.
  • Kies betekenisvolle relatiemethodenamen die consistent zijn in enkelvoud/meervoud: meervoud voor hasMany (comments), enkelvoud voor belongsTo (post).
  • Gebruik op overzichtsschermen vrijwel altijd with; op een detailpagina van één record laad je alleen wat je nodig hebt.

Veelgestelde vragen

Wat is het verschil tussen hasMany en belongsTo?

hasMany wordt gedefinieerd aan de "ouder"-kant van de relatie en vertegenwoordigt veel onderliggende records. belongsTo staat aan de "kind"-kant die de foreign key bevat en verwijst naar één bovenliggend record. Het zijn twee richtingen van dezelfde relatie.

Mag ik een foreign-keykolomnaam gebruiken die afwijkt van Eloquents aanname?

Ja. Geef de foreign key als tweede argument en de lokale sleutel als derde mee: hasMany(Comment::class, 'article_id', 'id'). Tenzij anders aangegeven gaat Eloquent uit van de modelnaam in enkelvoud + _id.

Moet ik altijd eager loading gebruiken?

Als je gerelateerde data in een lus benadert, ja — dat voorkomt het N+1-probleem. Maar als je de relatie nooit aanraakt, laad hem dan niet onnodig; dat kost extra query's en geheugen. Stem de balans af op je behoefte.

Relaties correct modelleren is de basis van een snelle, onderhoudbare Laravel-applicatie. Heb je hulp nodig met Eloquent-modellering, N+1-optimalisatie of een complete Laravel-backend voor je project, neem dan contact met me op en leggen we samen een solide fundering.

Devamı için