Laravel 11 for Beginners: Model Relationships and Eloquent ORM

Arlind Musliu Portrait
Arlind Musliu

January 4, 2024 · 7 min read · 892 views

Laravel Models Relationships Eloquent Data

2024 UPDATE - LARAVEL 11

We're excited to announce that we have updated all of our blog post examples to reflect the new Laravel 11 version! Our previous examples were based on Laravel 10, but with the release of Laravel 11, we wanted to ensure that our readers have access to the most up-to-date information and examples.

Laravel Eloquent ORM Relationships

Embarking on a Laravel adventure means you're going to deal with data, and not just any data, but data that are connected in all sorts of complex ways. That's where model relationships come in, linking everything together as the glue of your application's data.

Defining Relationships in Laravel

Laravel supports a variety of relationship types, allowing you to express the connections between your data models clearly. Here's a brief overview of each type:

  • One To One: This is a straightforward relationship where one record from a table corresponds to one record in another table. For instance, each user might have one unique profile.

  • One To Many: A common relationship where one record can be associated with multiple records in another table. For example, a user can have many posts.

  • Many To One: The inverse of one-to-many, where many records in one table relate to a single record in another table.

  • Many To Many: This relationship allows multiple records in one table to be associated with multiple records in another table. A user might subscribe to multiple magazines, and each magazine might have multiple subscribers. We should also create a pivot table to make it work.

  • Polymorphic Relationships: These allow a model to belong to more than one other model on a single association. An image might be associated with either a post or a user profile.

  • Many To Many Polymorphic Relationships: An extension of polymorphic relationships where a model can have multiple relations to multiple models.

  • Has One Through and Has Many Through: These relationships allow you to access distant relations via an intermediate relation. For example, a country might have many posts through a user model.

laravel pipelines

Let's Build an App with Laravel

Creating a blog with Laravel is a good example of beginning your journey into the world of web development. As your blog grows, so does the complexity of the relationships between your data. Understanding how to define and use these relationships in Laravel is key to building a feature-rich and efficient blogging platform.

Note: Do not worry about coding for now, we will start coding in the next lesson. Focus on understanding these concepts as they will help you in your Laravel journey.

The Blueprint of Our App

Let's define the main features of our blog:

  • Users: The authors who write blog posts.

  • Profiles: The profiles of the users.

  • Posts: The articles or blog entries published by users.

  • Tags: Keywords or topics that categorize posts.

  • Images: Photos or graphics associated with posts or users.

Now, let's explore how these entities relate to each other in a typical blog.

Creating the Relationships

One To One: User and Profile

Each user on our blog might have a detailed profile. This is a one-to-one relationship.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Authenticatable
{
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}

For the one-to-one relationship between a User and a Profile, the inverse would allow us to find the user from the profile.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Profile extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

One To Many: User and Posts

A user can write many blog posts, but each post is written by one user. This is a one-to-many relationship.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Authenticatable
{
	public function profile()
    {
        return $this->hasOne(Profile::class);
    }
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

The inverse of the one-to-many relationship between User and Posts would allow us to find the author of a specific post.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Many To Many: Posts and Tags

Table Structure

To define this relationship, three database tables are needed: posts, tags, and post_tag. The post_tag table is derived from the alphabetical order of the related model names and contains post_id and tag_id columns. This table is used as an intermediate table linking the posts and tags.

Remember, since a tag can belong to many users, we cannot simply place a tag_id column on the posts table. This would mean that a tag could only belong to a single post. In order to provide support for tags being assigned to multiple posts, the post_tag table is needed. We can summarize the relationship's table structure like so:

  • posts

    • id - integer

    • name - string

  • tags

    • id - integer

    • name - string

  • post_tag

    • post_id - integer

    • tag_id - integer

A post can be tagged with multiple keywords, and each tag can be applied to multiple posts. This is a many-to-many relationship. We need to add a new pivot table post_tag (mind the alphabetical order here) so that post_id and tag_id are both saved there. This will allow us to add as many items as needed.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
	public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}

For the many-to-many relationship between Posts and Tags, the inverse relationship would allow us to find all posts associated with a particular tag.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

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

Polymorphic: Images

An image might be associated with a user profile or a blog post. This is a polymorphic relationship.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Image extends Model
{
	protected $fillable = ['path'];

    public function imageable()
    {
        return $this->morphTo();
    }
}

The inverse relationship would have to be implemented on the Post and Profile model as well.

Let's start with the Post:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
	public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
    public function images()
    {
        return $this->morphMany(Image::class, 'imageable');
    }
}

We do the same for the Profile:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Profile extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
    public function images()
    {
        return $this->morphMany(Image::class, 'imageable');
    }
}
laravel pipelines

Using Relationships in Your App

To cover the different relationships in our blog application, we'll create several controllers, each with a clear purpose. This approach follows the Single Responsibility Principle and makes our application easier to maintain and understand.

UserController

The UserController handles operations related to users, such as displaying a user's profile and posts.

<?php

namespace App\Http\Controllers;

use App\Models\User;

class UserController extends Controller
{
	// Display all users
 	public function index()
    {
        $users = User::all();
        return view('users.index', ['users' => $users]);
    }

    // Display a user and their profile
    public function show($id)
    {
        $user = User::with('profile')->findOrFail($id);
        return view('users.show', ['user' => $user]);
    }
}

PostController

The PostController is concerned with blog post operations, including listing, creating, editing, and deleting posts.

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use App\Models\Tag;
use Illuminate\Http\Request;

class PostController extends Controller
{
    // Display all blog posts
    public function index()
    {
        $posts = Post::with('user', 'tags')->latest()->paginate();

        return view('posts.index', ['posts' => $posts]);
    }

    // Show a form for creating a new blog post
    public function create()
    {
        return view('posts.create');
    }

    // Store a newly created blog post
    public function store(Request $request)
    {
        $validatedData = $request->validate([
            'title' => 'required|max:255',
            'content' => 'required',
            // other validation rules...
        ]);

		$tags = $request->validate([
            'tags' => 'required',
        ]);

		// the new post belongs to the authenticated user
        $post = auth()->user()->posts()->create($validatedData);

		// we add tags to the post by using the sync() method
        $post->tags()->sync($tags);

        return redirect()->route('posts.show', $post);
    }

    // Display a single blog post
    public function show($id)
    {
        $post = Post::with('user', 'tags', 'images')
							->findOrFail($id);

        return view('posts.show', ['post' => $post]);
    }

    // Show a form for editing the specified blog post
    public function edit($id)
    {
        $post = Post::findOrFail($id);
        $tags = Tag::all();
        return view('posts.edit', ['post' => $post, 'tags' => $tags]);
    }

    // Update the specified blog post
    public function update(Request $request, Post $post)
    {
        $validatedData = $request->validate([
            'title' => 'required|max:255',
            'content' => 'required',
            // other validation rules...
        ]);

		$tags = $request->validate([
            'tags' => 'required',
        ]);

        $post->update($validatedData);

		// we modify the tags of the post by using the sync() method
        $post->tags()->sync($tags);

        return redirect()->route('posts.show', $post);
    }

    // Remove the specified blog post
    public function destroy(Post $post)
    {
        $post->delete();

        return redirect()->route('posts.index');
    }
}

TagController

The TagController handles tag-related actions, such as viewing all posts associated with a tag.

<?php

namespace App\Http\Controllers;

use App\Models\Tag;

class TagController extends Controller
{
    // Display all posts for a specific tag
    public function show($id)
    {
        $tag = Tag::with('posts')->findOrFail($id);

        return view('tags.show', ['tag' => $tag, 'posts' => $tag->posts]);
    }
}

Uploading Images (Polymorphic relationship)

Here's an example of how you might handle image uploads in a PostController. This is a simplified example, and in a real-world scenario, you would likely have more complex validation, storage, and error handling.

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use App\Models\Image;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class PostController extends Controller
{
    // ... (other methods)

    // Store a newly created blog post with an image
    public function store(Request $request)
    {
        $validatedData = $request->validate([
            'title' => 'required|max:255',
            'content' => 'required',
            'image' => 'nullable|image|max:2048', // 2MB Max
        ]);

		// we already showed how you can create a user post
        $post = auth()->user()->posts()->create($validatedData);

		// now let's add our image to the post we wrote
        if ($request->hasFile('image')) {
            $path = $request->file('image')->store('images', 'public');
            $post->images()->create(['path' => $path]);
        }

        return redirect()->route('posts.show', $post);
    }

    // ... (other methods)
}

In this store method, we're validating the incoming request, including an image file. If an image is included, we use Laravel's store method, which saves the file in the images directory within the public disk (public storage). We then create a new Image record associated with the post using the create method on the images relationship.

The 'public' parameter in the store method specifies the disk defined in config/filesystems.php, where you can set up different filesystems. The 'images' is a directory within that disk.

Remember to exectue command in terminal: php artisan storage:link also include enctype="multipart/form-data" in your <form> tag to allow file uploads.

Laravel best PHP framework

Conclusion

Model relationships in Laravel provide a powerful way to represent and manage the interconnected data of a blog. By defining clear relationships between users, posts, tags, and images, you can write code that's both efficient and expressive. Laravel's Eloquent ORM makes it easy to handle these relationships, allowing you to focus on crafting the perfect blogging experience.

Upcoming Articles in the Series

  1. Laravel for Beginners: Laravel Migrations

  2. Laravel for Beginners: Laravel Seeders and Factories

  3. Laravel for Beginners: Routing Your Application

This article is part of our series Laravel 11 for Beginners: A Step-by-Step Guide to Learn the Concepts.


Bring Your Ideas to Life 🚀

If you need help with a Laravel project let's get in touch.

Lucky Media is proud to be recognized as a Top Laravel Development Agency

Arlind Musliu Portrait
Arlind Musliu

Cofounder and CFO of Lucky Media

Technologies:

Laravel
Heading Pattern

Related Posts

Stay up to date

Be updated with all news, products and tips we share!