Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Model relationships

Database tables are related to each other: a post has an author and comments. In Eloquent a relationship is described by a model method, after which the related records are available as a regular property.

The foreign key

A relationship is stored in a column holding the identifier of another table. In the comments migration one line creates it:

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

cascadeOnDelete() removes the comments together with the post.

One to many

A post has many comments — the hasMany() method:

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

Related records are read as a property, without parentheses:

$post = Post::find(1);
 
foreach ($post->comments as $comment) {
    echo $comment->body;
}

The inverse relationship

A comment belongs to one post — the belongsTo() method:

class Comment extends Model
{
    public function post()
    {
        return $this->belongsTo(Post::class);
    }
}
echo $comment->post->title;

Through the relationship a record is created with the right post_id straight away:

$post->comments()->create(['body' => 'Great post!']);

Here the relationship is called as a method, with parentheses: that gives you a query builder rather than a finished list.

Many to many

A post has many tags, and a tag has many posts. This relationship needs a pivot table post_tag with the columns post_id and tag_id:

class Post extends Model
{
    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}
$post->tags()->attach($tagId);  // attach a tag
$post->tags()->detach($tagId);  // detach a tag

Eager loading

The with() method loads related records in advance, with one extra query:

$posts = Post::with('comments')->get();

Common mistake

Without with(), accessing a relationship in a loop runs a separate query for every record. A hundred posts mean a hundred and one queries — this is known as the N+1 problem.

// Slow: a comments query on every iteration.
foreach (Post::all() as $post) {
    echo $post->comments->count();
}
 
// Fast: just two queries.
foreach (Post::with('comments')->get() as $post) {
    echo $post->comments->count();
}

Thanks for reading!

Donate to the site — we work hard for you.