January 4, 2024 · 7 min read · 892 views
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.
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.
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.
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.
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.
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);
}
}
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);
}
}
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);
}
}
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');
}
}
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.
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]);
}
}
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');
}
}
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]);
}
}
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 includeenctype="multipart/form-data"
in your<form>
tag to allow file uploads.
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.
This article is part of our series Laravel 11 for Beginners: A Step-by-Step Guide to Learn the Concepts.
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
Technologies:
Related Posts
Stay up to date
Be updated with all news, products and tips we share!