id}:{$sessionKey}"; if (Cache::has($cacheKey)) { return false; // already counted this hour } Cache::put($cacheKey, 1, now()->addHour()); Post::withoutTimestamps(function () use ($post) { DB::table('posts') ->where('id', $post->id) ->increment('impressions_count'); }); // Recompute engagement score asynchronously via a quick DB update $this->refreshEngagementScore($post->id); return true; } /** * Refresh the cached engagement_score = (reactions*2 + comments*3 + saves) / max(impressions, 1) */ public function refreshEngagementScore(int $postId): void { Post::withoutTimestamps(function () use ($postId) { DB::table('posts') ->where('id', $postId) ->update([ 'engagement_score' => DB::raw( '(reactions_count * 2 + comments_count * 3 + saves_count) / GREATEST(impressions_count, 1)' ), ]); }); } /** * Return analytics summary for a post (owner view). */ public function getSummary(Post $post): array { $reactions = $post->reactions_count; $comments = $post->comments_count; $saves = $post->saves_count; $impressions = $post->impressions_count; $rate = $impressions > 0 ? round((($reactions + $comments + $saves) / $impressions) * 100, 2) : 0.0; return [ 'impressions' => $impressions, 'reactions' => $reactions, 'comments' => $comments, 'saves' => $saves, 'engagement_rate' => $rate, // percentage 'engagement_score' => round($post->engagement_score, 4), ]; } }