Laravel101: Exploring Entity-Model Relationships
Up until now, we’ve built an awesome app that lets users save tasks. But now, we want to take it a step further and save each user’s tasks individually. That means each user will have their own set of tasks. And of course, we’ll make sure to relate the tasks with the current user who’s logged into the app. So, they’ll have easy access to the their tasks. Sounds cool, right?
Relational Database Management Systems (RDBMS) are a type of database management system that work based on relationships. Basically, data and relationships are organized using a set of related tables. Many popular DBMSs like SQLite, MySQL, and Microsoft Access follow this structure.
Now, talking about different types of database management structures is beyond the scope of this article. However, if you’re curious and want to explore more about the various types of DBMS, you can check out the following link:
Alright, let’s dive into the topic of defining relationships in models.
When it comes to table relationships, the main ones we encounter are one-to-many and one-to-one. In a one-to-one relationship, each record in one table is linked to just one record in another table. For instance, think of the connection between a student’s and their account details:
On the other hand, in a one-to-many relationship, each record in one table can be associated with multiple records in another table. Take a peek at the image below:
you’ll notice that each customer can have several orders, but each order belongs to only one customer.
And hey, let’s not forget about another type of relationship called many-to-many. To handle this, we convert it into two one-to-many relationships using an interface table. Check out the image below:
you’ll see a many-to-many relationship between the courses and students tables, which we’ve transformed using an interface table with two one-to-many relationships.
In our example, each user is associated with their own set of tasks.
Here’s how it works: a user can have multiple tasks, but each task belongs to only one user. So, to make this happen, we need to update our tasks model migration file and introduce a new attribute called foreignId
. This attribute will be responsible for storing connection between a user to the task.
To establish this connection, we can use any unique data from users table. In our design, we have options like email and ID.
Here’s a helpful tip: in relational databases, we often use the ID since it optimized retrieval of related models.
Let’s update our migration file and add foreignId
:
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->foreignId('user_id')->constrained();
$table->timestamp('expired_at')->nullable();
$table->timestamps();
});
As you might have noticed, I’ve used a handy function called constrained
here. Its main purpose is to establish a relationship between models in a table. This function adds extra information to attributes, such as the related model’s table name. In this case, the related model is the users
table.
You can actually specify the table name explicitly like constrained('users')
but this function is smart enough to figure it out automatically based on the foreignId
attribute name.
Now we update our migration file let update our tables:
php artisan migrate:fresh --force
Well done! Now, let’s talk about how we can actually use this in our project.
In Laravel, Eloquent provides some handy helper functions to define relationships. In our case, since the task model belongs to a user, we can simply define the belongsTo
function in tasks model:
class Task extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
On the other hand, for our user, which can have many tasks, we can use the hasMany
function:
class User extends Authenticatable
{
public function tasks()
{
return $this->hasMany(Task::class);
}
}
Of course we doesn’t have many-to-many in our project yet, but let’s talk about this exciting relationship. The good news is that it’s simpler for both models! We only need to use the belongsToMany
function for both of them. These functions are usually all we need to define relationships. But if you’re looking for more detailed explanations, you can find them right here:
These functions have some really handy applications. Let’s say we have a task and we want to know the name of the user who created it. In this case, here’s how we can tackle it:
$task = Task::find(1);
echo $task->user->name;
Here’s the thing you should know: In our Task model, we’ve got this cool function called “user” defined. Now, you might be wondering why we’re using it as an attribute, without those parentheses.
Well, let me explain. When we treat these functions as attributes, it’s almost like we’re directly referring to the user model itself using a query. That’s why we can easily access attributes like the user’s name. It’s a convenient way to get the information we need right away!
Let’s practice it with tasks function:
As you see in above pic you can find all the relevant models for a specific user or perform any query on the task models that are associated with that particular user.
Now, let’s back to our task controller and update the store function. The goal is to create a new task for the current user and set the user ID attribute. We can achieve this in a very direct way with the following code: (auth()->id()
get current logged in user id):
$task = new Task();
$task->title = request('title');
$task->descripton = request('descripton');
$task->expired_at = request('expired_at');
$task->user_id = auth()->id();
$task->save();
However, let me introduce you to an even cooler option using the tasks
function. This approach offers some amazing benefits. Check it out:
public function store(StoreTaskRequest $request)
{
auth()->user()->tasks()->create($request->validated());
return redirect("/tasks", 201);
}
Here the foreign id of user is handled by tasks
That’s awesome! Now, let’s update our index function to retrieve all the tasks associated with the currently logged-in user. It’s really simple. Just update the index function like this:
public function index()
{
return view('tasks.index', ['tasks' => auth()->user()->tasks]);
}
Also we can make use of the auth functions in Blade templates to directly access the tasks of the current user. This means we can remove the task variable passing from the index function. Instead, we can fetch all the tasks within the Blade template itself. It’s super convenient!
In this article, we’ve covered the relationships between users and tasks models in Laravel. The information we discussed is not only important but also highly practical. Mastering complex queries is a key skill for anyone working in the backend. I hope you found this tutorial enjoyable and insightful.