Skip to content

Commit 774ae79

Browse files
committed
add snippet views tracking and update snippets display with views and favorites count
1 parent 92c0352 commit 774ae79

File tree

13 files changed

+336
-146
lines changed

13 files changed

+336
-146
lines changed

app/Http/Controllers/DashboardController.php

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,49 @@
33
namespace App\Http\Controllers;
44

55
use App\Models\Snippet;
6+
use App\Models\User;
67
use Illuminate\Http\Request;
78
use Illuminate\Support\Facades\DB;
9+
use Illuminate\Support\Facades\Log;
10+
use Illuminate\Support\Facades\Auth;
811
use Inertia\Inertia;
12+
use Inertia\Response;
913

1014
class DashboardController extends Controller
1115
{
1216
public function index()
1317
{
14-
$user = auth()->user();
15-
16-
// Get total snippets for the user
17-
$totalSnippets = Snippet::where('user_id', $user->id)->count();
18-
19-
// Get total views across all user snippets
20-
$totalViews = Snippet::where('user_id', $user->id)->sum('views_count');
21-
22-
// Get total favorites received
23-
$totalFavorites = Snippet::where('user_id', $user->id)->sum('favorites_count');
24-
25-
// Get monthly views for the past year
26-
$monthlyViews = Snippet::where('user_id', $user->id)
27-
->selectRaw('DATE_FORMAT(created_at, "%b") as month, SUM(views_count) as views')
28-
->whereYear('created_at', now()->year)
29-
->groupBy('month')
30-
->orderBy(DB::raw('MONTH(created_at)'))
31-
->pluck('views', 'month')
32-
->toArray();
33-
34-
// Get popular snippets
35-
$popularSnippets = Snippet::where('user_id', $user->id)
36-
->orderBy('views_count', 'desc')
37-
->limit(10)
38-
->get(['id', 'title', 'views_count as views', 'language'])
39-
->toArray();
40-
41-
return Inertia::render('Dashboard', [
42-
'dashboardData' => [
43-
'totalSnippets' => $totalSnippets,
44-
'totalViews' => $totalViews,
45-
'totalFavorites' => $totalFavorites,
46-
'monthlyViews' => $monthlyViews,
47-
'popularSnippets' => $popularSnippets,
48-
]
49-
]);
18+
// $user = auth()->user();
19+
20+
// $totalSnippets = Snippet::where('user_id', $user->id)->count();
21+
22+
// $totalViews = Snippet::where('user_id', $user->id)->sum('views_count');
23+
24+
// $totalFavorites = Snippet::where('user_id', $user->id)->sum('favorites_count');
25+
26+
// $monthlyViews = Snippet::where('user_id', $user->id)
27+
// ->selectRaw("strftime('%m', created_at) as month_num, strftime('%b', created_at) as month, SUM(views_count) as views")
28+
// ->whereRaw("strftime('%Y', created_at) = ?", [now()->year])
29+
// ->groupBy('month')
30+
// ->orderBy('month_num')
31+
// ->pluck('views', 'month')
32+
// ->toArray();
33+
34+
// $popularSnippets = Snippet::where('user_id', $user->id)
35+
// ->orderBy('views_count', 'desc')
36+
// ->limit(10)
37+
// ->get(['id', 'title', 'views_count as views', 'language'])
38+
// ->toArray();
39+
40+
// $dashboardData = [
41+
// 'totalSnippets' => $totalSnippets,
42+
// 'totalViews' => $totalViews,
43+
// 'totalFavorites' => $totalFavorites,
44+
// 'monthlyViews' => $monthlyViews,
45+
// 'popularSnippets' => $popularSnippets,
46+
// ];
47+
// return Inertia::render('Dashboard', [
48+
// 'dashboardData' => $dashboardData
49+
// ]);
5050
}
5151
}

app/Http/Controllers/SnippetController.php

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Http\Controllers;
44

55
use App\Models\Snippet;
6+
use App\Models\SnippetView;
67
use Illuminate\Http\Request;
78
use Inertia\Inertia;
89
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
@@ -59,14 +60,36 @@ public function store(Request $request)
5960
/**
6061
* Display the specified resource.
6162
*/
62-
public function show(Snippet $snippet)
63+
public function show(Snippet $snippet, Request $request)
6364
{
6465
if (!$snippet->is_public && $snippet->user_id !== auth()->id()) {
6566
abort(403);
6667
}
6768

6869
$snippet->load('user');
69-
$snippet->increment('views_count');
70+
71+
// Get visitor IP address
72+
$visitorIp = $request->ip();
73+
74+
// Check if this is a new view (not from the same IP in the last hour)
75+
$recentView = SnippetView::where('snippet_id', $snippet->id)
76+
->where('viewer_ip', $visitorIp)
77+
->where('viewed_at', '>=', now()->subHour())
78+
->exists();
79+
80+
// Only count as a new view if it's not a recent duplicate
81+
if (!$recentView) {
82+
// Record the view in the new snippet_views table
83+
SnippetView::create([
84+
'snippet_id' => $snippet->id,
85+
'viewed_at' => now(),
86+
'viewer_ip' => $visitorIp,
87+
]);
88+
89+
// Also increment the views_count for backwards compatibility
90+
$snippet->increment('views_count');
91+
}
92+
7093
// Add is_favorite check for authenticated users
7194
if (auth()->check()) {
7295
$snippet->is_favorite = $snippet->favoritedBy()->where('user_id', auth()->id())->exists();
@@ -119,7 +142,7 @@ public function destroy(Snippet $snippet)
119142
$this->authorize('delete', $snippet);
120143

121144
$snippet->delete();
122-
145+
123146
}
124147

125148
public function toggleFavorite(Snippet $snippet)

app/Http/Middleware/HandleInertiaRequests.php

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
use Illuminate\Foundation\Inspiring;
66
use Illuminate\Http\Request;
77
use Inertia\Middleware;
8-
use Tighten\Ziggy\Ziggy;
8+
use App\Models\Snippet;
9+
use App\Models\SnippetView;
10+
use App\Models\User;
11+
use Illuminate\Support\Facades\DB;
12+
913

1014
class HandleInertiaRequests extends Middleware
1115
{
@@ -39,17 +43,39 @@ public function share(Request $request): array
3943
{
4044
[$message, $author] = str(Inspiring::quotes()->random())->explode('-');
4145

42-
return [
46+
$sharedData = [
4347
...parent::share($request),
4448
'name' => config('app.name'),
4549
'auth' => [
4650
'user' => $request->user(),
4751
],
4852
'quote' => ['message' => trim($message), 'author' => trim($author)],
49-
'ziggy' => [
50-
...(new Ziggy)->toArray(),
51-
'location' => $request->url(),
52-
],
5353
];
54+
55+
// Only add dashboardData if user is authenticated
56+
if ($request->user()) {
57+
$user = $request->user();
58+
$realMonthlyViews = SnippetView::join('snippets', 'snippets.id', '=', 'snippet_views.snippet_id')
59+
->where('snippets.user_id', $user->id)
60+
->whereYear('viewed_at', now()->year)
61+
->selectRaw("strftime('%m', viewed_at) as month_num, strftime('%b', viewed_at) as month, COUNT(*) as views")
62+
->groupBy('month', 'month_num')
63+
->orderBy('month_num')
64+
->pluck('views', 'month')
65+
->toArray();
66+
$sharedData['dashboardData'] = [
67+
'totalSnippets' => Snippet::where('user_id', $user->id)->count(),
68+
'totalViews' => Snippet::where('user_id', $user->id)->sum('views_count'),
69+
'totalFavorites' => Snippet::where('user_id', $user->id)->sum('favorites_count'),
70+
'monthlyViews' => $realMonthlyViews,
71+
'popularSnippets' => Snippet::where('user_id', $user->id)
72+
->orderBy('views_count', 'desc')
73+
->limit(10)
74+
->get(['id', 'title', 'views_count as views', 'language'])
75+
->toArray(),
76+
];
77+
}
78+
79+
return $sharedData;
5480
}
5581
}

app/Models/Snippet.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ class Snippet extends Model
1818
'language',
1919
'is_public',
2020
'user_id',
21+
'views_count', // Add this
22+
'favorites_count', // Add this
2123
];
2224

2325
protected $casts = [
2426
'is_public' => 'boolean',
27+
'views_count' => 'integer', // Add this
28+
'favorites_count' => 'integer' // Add this
2529
];
2630

2731
public function user(): BelongsTo

app/Models/SnippetView.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Factories\HasFactory;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class SnippetView extends Model
9+
{
10+
use HasFactory;
11+
12+
protected $fillable = [
13+
'snippet_id',
14+
'viewed_at',
15+
'viewer_ip',
16+
];
17+
18+
protected $casts = [
19+
'viewed_at' => 'datetime',
20+
];
21+
22+
public function snippet()
23+
{
24+
return $this->belongsTo(Snippet::class);
25+
}
26+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::create('snippet_views', function (Blueprint $table) {
15+
$table->id();
16+
$table->foreignId('snippet_id')->constrained()->onDelete('cascade');
17+
$table->timestamp('viewed_at');
18+
$table->string('viewer_ip')->nullable();
19+
$table->index('viewed_at');
20+
$table->index(['snippet_id', 'viewed_at']);
21+
$table->timestamps();
22+
});
23+
}
24+
25+
/**
26+
* Reverse the migrations.
27+
*/
28+
public function down(): void
29+
{
30+
Schema::dropIfExists('snippet_views');
31+
}
32+
};

database/seeders/DatabaseSeeder.php

Lines changed: 87 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,102 @@
22

33
namespace Database\Seeders;
44

5+
use App\Models\Snippet;
6+
use App\Models\SnippetView;
57
use App\Models\User;
6-
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
7-
use Database\Seeders\SnippetSeeder;
88
use Illuminate\Database\Seeder;
9+
use Carbon\Carbon;
10+
use Illuminate\Support\Facades\Hash;
11+
912

1013
class DatabaseSeeder extends Seeder
1114
{
1215
/**
13-
* Seed the application's database.
16+
* Run the database seeds.
1417
*/
1518
public function run(): void
1619
{
17-
// User::factory(10)->create();
18-
19-
// User::factory()->create([
20-
// 'name' => 'Test User',
21-
// 'email' => 'test@example.com',
22-
// ]);
23-
$this->call([
24-
SnippetSeeder::class,
20+
// Create a test user if none exists
21+
$user = User::firstOrCreate(
22+
['email' => 'test@example.com'],
23+
[
24+
'name' => 'Test User',
25+
'password' => Hash::make('password'),
26+
'email_verified_at' => now(),
27+
]
28+
);
29+
30+
// Create a sample snippet
31+
$snippet = Snippet::create([
32+
'title' => 'Example Sorting Algorithm',
33+
'description' => 'A simple sorting algorithm implementation for demonstration purposes',
34+
'code' => "function bubbleSort(arr) {\n const n = arr.length;\n \n for (let i = 0; i < n; i++) {\n for (let j = 0; j < n - i - 1; j++) {\n if (arr[j] > arr[j + 1]) {\n // Swap elements\n [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];\n }\n }\n }\n \n return arr;\n}",
35+
'language' => 'javascript',
36+
'is_public' => true,
37+
'user_id' => $user->id,
38+
'views_count' => 0,
39+
'favorites_count' => rand(5, 20),
2540
]);
41+
42+
// Generate views for each month (January to current month)
43+
$this->generateMonthlyViews($snippet);
44+
45+
$this->command->info('Test snippet created with ID: ' . $snippet->id);
46+
$this->command->info('Monthly views have been generated for the test snippet');
47+
}
48+
49+
/**
50+
* Generate monthly views for a snippet
51+
*/
52+
private function generateMonthlyViews(Snippet $snippet): void
53+
{
54+
// Get current month and year
55+
$currentMonth = Carbon::now()->month;
56+
$currentYear = Carbon::now()->year;
57+
58+
// Set up IP addresses for variety
59+
$ipAddresses = [
60+
'192.168.1.1', '192.168.1.2', '192.168.1.3',
61+
'192.168.1.4', '192.168.1.5', '192.168.1.6',
62+
'172.16.0.1', '172.16.0.2', '172.16.0.3',
63+
'10.0.0.1', '10.0.0.2', '10.0.0.3'
64+
];
65+
66+
$viewsCount = 0;
67+
68+
// Generate views for each month from January to current month
69+
for ($month = 1; $month <= $currentMonth; $month++) {
70+
// Generate a random number of views for this month (increasing trend)
71+
$monthlyViews = rand(20, 50) + ($month * 15); // More views in later months
72+
73+
// For each view in this month
74+
for ($i = 0; $i < $monthlyViews; $i++) {
75+
// Create a random date within the month
76+
$day = rand(1, Carbon::create($currentYear, $month)->daysInMonth);
77+
$hour = rand(0, 23);
78+
$minute = rand(0, 59);
79+
80+
$viewDate = Carbon::create($currentYear, $month, $day, $hour, $minute);
81+
82+
// Skip future dates
83+
if ($viewDate->isFuture()) {
84+
continue;
85+
}
86+
87+
// Create the view record
88+
SnippetView::create([
89+
'snippet_id' => $snippet->id,
90+
'viewed_at' => $viewDate,
91+
'viewer_ip' => $ipAddresses[array_rand($ipAddresses)],
92+
'created_at' => $viewDate,
93+
'updated_at' => $viewDate,
94+
]);
95+
96+
$viewsCount++;
97+
}
98+
}
99+
100+
// Update the snippet's views_count
101+
$snippet->update(['views_count' => $viewsCount]);
26102
}
27103
}

resources/js/components/UserMenuContent.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ defineProps<Props>();
3434
<Link
3535
class="block w-full"
3636
:href="user? route('logout') : route('login')"
37-
method="post"
37+
:method="user ? 'post' : undefined"
3838
as="button"
3939
>
4040
<LogOut class="mr-2 w-4 h-4" v-if="user" />

0 commit comments

Comments
 (0)