Notifications table design?
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Designing a Robust Notification System in Laravel: From Relationships to Logs
Building a social network requires more than just storing user data; it demands a sophisticated system for tracking interactions. When you are designing a notification system, the key challenge isn't just creating a table, but establishing the correct relationships between users and events. As a senior developer working with the Laravel ecosystem, understanding how to structure these data models correctly is paramount for scalability and performance.
Let’s dive into your specific problem: how to handle actions like following and commenting, and how to manage the resulting notifications efficiently.
The Foundation: Separating Relationships from Notifications
Your initial table design is a good start, but it mixes the notification content with the core relationship tracking. A more robust system separates these concerns into distinct entities. For social networking, we need at least two primary components: Relationships (who follows whom) and Notifications (the log of events).
1. Modeling User Relationships (The Follows Table)
For actions like following, the most efficient way to track this is through a many-to-many relationship. This prevents redundant data entry and makes querying relationships extremely fast.
We need a dedicated table to manage who follows whom:
CREATE TABLE IF NOT EXISTS `follows` (
`follower_id` int(11) NOT NULL,
`following_id` int(11) NOT NULL,
PRIMARY KEY (`follower_id`, `following_id`),
FOREIGN KEY (`follower_id`) REFERENCES `users`(`id`),
FOREIGN KEY (`following_id`) REFERENCES `users`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
When User A follows User B, you insert a single record into the follows table. This relationship is immutable unless they unfollow each other, and it serves as the source of truth for all subsequent notification triggers.
2. Designing the Notification Log Table
Your existing notifications table is designed to be a log. It should focus purely on what the user needs to see, not how the relationship was formed.
Here is an enhanced structure that links directly to the source of the event:
CREATE TABLE IF NOT EXISTS `notifications` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL, -- The user who will receive the notification
`type` varchar(50) NOT NULL, -- e.g., 'followed', 'commented', 'liked'
`target_id` int(11) NOT NULL, -- The ID of the related entity (e.g., the post ID or the user ID who performed the action)
`actor_id` int(11) NOT NULL, -- The ID of the user who caused the notification (the follower/commenter)
`message` text, -- The actual content to display ("X commented on your post.")
`read` boolean DEFAULT FALSE, -- Status of the notification
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Notice how we've added type, target_id, and actor_id. This structure allows you to query notifications very efficiently. For example, finding all notifications for a user involves simple queries against this table, which is crucial for good performance when dealing with large datasets, especially within the Laravel framework where Eloquent relationships are so powerful.
Implementing Logic in Laravel (The Trigger Mechanism)
The confusion lies in when to insert into the notifications table. The rule should be: Insert a notification record immediately after the relationship or event is successfully recorded.
Example: Handling a Follow Event
When User A clicks the "Follow" button on User B's profile page, your controller logic must execute these steps:
- Check Relationship: Verify if User A is already following User B (using the
followstable). - Create Relationship: If not followed, insert a new record into the
followstable (follower_id = A,following_id = B). - Trigger Notification: Immediately create a notification record in the
notificationstable for User B (the person who was followed).
Here is a conceptual Laravel implementation using Eloquent models:
use App\Models\Follow;
use App\Models\Notification;
// Assuming $followerId and $followingId are available from the request
$followerId = auth()->id();
$followingId = $request->following_user_id;
// 1. Create the relationship record
$follow = Follow::create([
'follower_id' => $followerId,
'following_id' => $followingId
]);
// 2. Trigger the notification for the followed user
Notification::create([
'user_id' => $followingId, // The recipient is the one who was followed
'type' => 'followed',
'target_id' => $followerId,
'actor_id' => $followerId,
'message' => "{$followerId} started following you.",
]);
return response()->json(['message' => 'Follow successful and notification sent.']);
Conclusion
Designing a notification system is fundamentally about managing state transitions efficiently. By separating your data into explicit relationship tables (like follows) and an event log table (notifications), you create a schema that is easy to query, maintainable, and highly scalable. When developing with Laravel, leveraging Eloquent relationships to manage these actions ensures that the database structure perfectly mirrors the application logic, leading to a robust and predictable user experience. Always prioritize clear relationships before logging events—this is a core principle of solid application architecture.