Workflow
- Voting: Users can upvote photos, and their votes are stored in Redis as part of a sorted set (
ZSET
). - Views: Each time a photo is viewed, the view count is incremented in Redis.
- Leaderboard: A combined score (e.g., based on votes and views) is calculated and stored for real-time ranking.
Setup Requirements
-
Ensure Redis is configured and connected in your Laravel project:
- In
.env
:CACHE_DRIVER=redis REDIS_HOST=127.0.0.1 REDIS_PORT=6379
- Install Redis support for Laravel if not already installed:
composer require predis/predis
- In
-
Add the Redis facade if needed:
use Illuminate\Support\Facades\Redis;
Code Implementation
Voting Logic
This method increments the vote count for a photo and updates the leaderboard score.
public function voteForPhoto($userId, $photoId)
{
$voteKey = "photo:votes:$userId"; // Track user votes
$leaderboardKey = "photo:leaderboard"; // Redis ZSET for ranking
// Check if the user has already voted for this photo
if (Redis::hexists($voteKey, $photoId)) {
return response()->json(['success' => false, 'message' => 'You have already voted for this photo.']);
}
// Increment the photo's score in the leaderboard
Redis::zincrby($leaderboardKey, 1, $photoId);
// Mark the photo as voted by the user
Redis::hset($voteKey, $photoId, 1);
return response()->json(['success' => true, 'message' => 'Vote recorded successfully.']);
}
View Tracking Logic
This method increments the view count for a photo.
public function recordPhotoView($photoId)
{
$viewsKey = "photo:views"; // Redis HASH for view counts
// Increment the view count for the photo
Redis::hincrby($viewsKey, $photoId, 1);
return response()->json(['success' => true, 'message' => 'View recorded successfully.']);
}
Retrieving the Leaderboard
This method fetches the top N photos from the leaderboard.
public function getLeaderboard($limit = 10)
{
$leaderboardKey = "photo:leaderboard";
// Get the top N photos from the leaderboard
$leaderboard = Redis::zrevrange($leaderboardKey, 0, $limit - 1, ['WITHSCORES' => true]);
// Format the result for easier readability
$formattedLeaderboard = [];
foreach ($leaderboard as $photoId => $score) {
$formattedLeaderboard[] = [
'photo_id' => $photoId,
'score' => $score,
];
}
return response()->json($formattedLeaderboard);
}
Combining Votes and Views for Scoring
If you want to dynamically calculate the leaderboard score based on a combination of votes and views (e.g., score = votes * 0.7 + views * 0.3
), you can create a method to recompute scores periodically.
public function recalculateLeaderboardScores()
{
$leaderboardKey = "photo:leaderboard";
$viewsKey = "photo:views";
// Get all photos from the leaderboard
$photos = Redis::zrange($leaderboardKey, 0, -1);
foreach ($photos as $photoId) {
// Get votes and views for each photo
$votes = Redis::zscore($leaderboardKey, $photoId) ?? 0;
$views = Redis::hget($viewsKey, $photoId) ?? 0;
// Recalculate the score
$newScore = ($votes * 0.7) + ($views * 0.3);
// Update the score in the leaderboard
Redis::zadd($leaderboardKey, ['NX'], $newScore, $photoId);
}
return response()->json(['success' => true, 'message' => 'Leaderboard scores recalculated.']);
}
API Endpoints Example
In your Laravel routes/api.php
, you can define endpoints like this:
use App\Http\Controllers\LeaderboardController;
Route::post('/vote', [LeaderboardController::class, 'voteForPhoto']);
Route::post('/view', [LeaderboardController::class, 'recordPhotoView']);
Route::get('/leaderboard', [LeaderboardController::class, 'getLeaderboard']);
Route::post('/leaderboard/recalculate', [LeaderboardController::class, 'recalculateLeaderboardScores']);
Example Usage
-
Voting for a Photo
- API Call:
POST /vote
- Request Body:
{ "userId": "123", "photoId": "456" }
- Request Body:
- API Call:
-
Recording a View
- API Call:
POST /view
- Request Body:
{ "photoId": "456" }
- Request Body:
- API Call:
-
Getting the Leaderboard
- API Call:
GET /leaderboard
- Response:
[ { "photo_id": "456", "score": "10" }, { "photo_id": "789", "score": "8" } ]
- Response:
- API Call:
-
Recalculating Leaderboard Scores
- API Call:
POST /leaderboard/recalculate
- API Call:
Benefits of Redis in Laravel
- Real-Time Performance: Redis ensures low latency for updates and reads.
- Scalable: Easily handles a growing number of votes, views, and leaderboard entries.
- Integration: Laravel’s Redis support makes the implementation seamless.
This setup provides a robust foundation for a real-time photo leaderboard system.