From 497deeef1825e1f0b94c3cc1b680ff2c2e33064d Mon Sep 17 00:00:00 2001 From: myuqinurrohman Date: Mon, 31 Mar 2025 14:40:05 +0700 Subject: [PATCH] feat: visitor counter --- .../Homepage/AboutUsController.php | 18 -- .../Homepage/AnnouncementController.php | 46 ----- .../Homepage/ContactController.php | 18 -- .../Homepage/LandingPageController.php | 57 ------ .../Controllers/Homepage/NewsController.php | 48 ----- app/Http/Controllers/HomepageController.php | 189 ++++++++++++++++++ .../Middleware/VisitorCounterMiddleware.php | 34 ++++ app/Models/Visitor.php | 16 ++ bootstrap/app.php | 2 + ...025_03_30_160236_create_visitors_table.php | 29 +++ database/seeders/VisitorSeeder.php | 17 ++ .../views/partials/homepage/footer.blade.php | 6 +- routes/web.php | 32 ++- 13 files changed, 304 insertions(+), 208 deletions(-) delete mode 100644 app/Http/Controllers/Homepage/AboutUsController.php delete mode 100644 app/Http/Controllers/Homepage/AnnouncementController.php delete mode 100644 app/Http/Controllers/Homepage/ContactController.php delete mode 100644 app/Http/Controllers/Homepage/LandingPageController.php delete mode 100644 app/Http/Controllers/Homepage/NewsController.php create mode 100644 app/Http/Controllers/HomepageController.php create mode 100644 app/Http/Middleware/VisitorCounterMiddleware.php create mode 100644 app/Models/Visitor.php create mode 100644 database/migrations/2025_03_30_160236_create_visitors_table.php create mode 100644 database/seeders/VisitorSeeder.php diff --git a/app/Http/Controllers/Homepage/AboutUsController.php b/app/Http/Controllers/Homepage/AboutUsController.php deleted file mode 100644 index 99398ea..0000000 --- a/app/Http/Controllers/Homepage/AboutUsController.php +++ /dev/null @@ -1,18 +0,0 @@ - 'Tentang Kami', - 'page' => 'about_us' - ]; - - return view('homepage.about-us.index', $data); - } -} diff --git a/app/Http/Controllers/Homepage/AnnouncementController.php b/app/Http/Controllers/Homepage/AnnouncementController.php deleted file mode 100644 index fac8a54..0000000 --- a/app/Http/Controllers/Homepage/AnnouncementController.php +++ /dev/null @@ -1,46 +0,0 @@ - 'Pengumuman', - 'page' => 'announcements', - ]; - - $search = $request->input('search', null); - - if($search){ - $data['announcements'] = News::where('category', '1')->where('title', 'LIKE', "%{$search}%") - // ->orWhere('content', 'LIKE', "%{$search}%") - ->latest() - ->simplePaginate(6); - }else{ - $data['announcements'] = News::where('category', '1')->latest()->simplePaginate(6); - } - - return view('homepage.announcements.index', $data); - } - - public function read($slug){ - $announcement = News::where('slug', $slug)->first(); - $announcement->increment('views', 1); - - $data = [ - 'title' => 'Pengumuman', - 'page' => 'announcements', - 'announcement' => $announcement, - 'otherAnnouncements' => News::where('category', '1')->orderByDesc('id')->where('id', '!=', $announcement->id)->take(4)->get() - ]; - - return view('homepage.announcements.read', $data); - - } -} diff --git a/app/Http/Controllers/Homepage/ContactController.php b/app/Http/Controllers/Homepage/ContactController.php deleted file mode 100644 index 5785441..0000000 --- a/app/Http/Controllers/Homepage/ContactController.php +++ /dev/null @@ -1,18 +0,0 @@ - 'Kontak', - 'page' => 'contacts' - ]; - - return view('homepage.contacts.index', $data); - } -} diff --git a/app/Http/Controllers/Homepage/LandingPageController.php b/app/Http/Controllers/Homepage/LandingPageController.php deleted file mode 100644 index 2effba8..0000000 --- a/app/Http/Controllers/Homepage/LandingPageController.php +++ /dev/null @@ -1,57 +0,0 @@ - 'Beranda', - 'page' => 'landing', - 'news' => News::whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()]) - ->where('category', '=', '0') - ->latest() - ->get(), - 'announcements' => News::whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()]) - ->where('category', '=', '1') - ->latest() - ->get(), - ]; - - return view('homepage.index', $data); - } - - public function search(Request $request){ - - $request->validate([ - 'search' => 'nullable|string|max:255|regex:/^[a-zA-Z0-9\s]+$/' - ], [ - 'search.string' => 'Pencarian harus berupa teks.', - 'search.max' => 'Pencarian tidak boleh lebih dari 255 karakter.', - 'search.regex' => 'Pencarian hanya boleh mengandung huruf, angka, dan spasi.' - ]); - - $search = trim($request->input('search')); - - $data = [ - 'title' => 'Pencarian', - 'page' => 'search', - 'data' => News::query() - ->when($search, function ($query, $search) { - return $query->where('title', 'LIKE', '%' . $search . '%'); - }) - ->latest() - ->simplePaginate(12) - ->appends(['search' => $search]), - 'search' => $search - ]; - - return view('homepage.search', $data); - } -} diff --git a/app/Http/Controllers/Homepage/NewsController.php b/app/Http/Controllers/Homepage/NewsController.php deleted file mode 100644 index 2617c86..0000000 --- a/app/Http/Controllers/Homepage/NewsController.php +++ /dev/null @@ -1,48 +0,0 @@ - 'Berita', - 'page' => 'news', - ]; - - $search = $request->input('search', null); - - if($search){ - $data['news'] = News::where('title', 'LIKE', "%{$search}%") - // ->orWhere('content', 'LIKE', "%{$search}%") - ->where('category', '0') - ->latest() - ->simplePaginate(12); - }else{ - $data['news'] = News::where('category', '0')->latest()->simplePaginate(12); - } - - return view('homepage.news.index', $data); - } - - public function read($slug){ - $news = News::where('slug', $slug)->first(); - $news->increment('views', 1); - - $data = [ - 'title' => 'Berita', - 'page' => 'news', - 'news' => $news, - 'popularNews' => News::where('category', '0')->orderBy('views', 'desc')->take(5)->get(), - 'otherNews' => News::where('category', '0')->orderByDesc('id')->where('id', '!=', $news->id)->take(3)->get() - ]; - - return view('homepage.news.read', $data); - - } -} diff --git a/app/Http/Controllers/HomepageController.php b/app/Http/Controllers/HomepageController.php new file mode 100644 index 0000000..41c04ad --- /dev/null +++ b/app/Http/Controllers/HomepageController.php @@ -0,0 +1,189 @@ +startOfMonth(); + $thisYear = Carbon::now()->startOfYear(); + + $this->yesterdayVisitor = DB::table('visitors') + ->whereDate('date', '=', $yesterday->toDateString()) + ->value('visitors'); + + $this->todayVisitor = DB::table('visitors') + ->whereDate('date', '=', $today->toDateString()) + ->value('visitors'); + + $this->thisMonthVisitor = DB::table('visitors') + ->whereBetween('date', [$thisMonth, Carbon::now()]) + ->sum('visitors'); + + $this->thisYearVisitor = DB::table('visitors') + ->whereBetween('date', [$thisYear, Carbon::now()]) + ->sum('visitors'); + + $this->totalVisitor = DB::table('visitors')->sum('visitors'); + + View::share('yesterdayVisitor', $this->yesterdayVisitor); + View::share('todayVisitor', $this->todayVisitor); + View::share('thisMonthVisitor', $this->thisMonthVisitor); + View::share('thisYearVisitor', $this->thisYearVisitor); + View::share('totalVisitor', $this->totalVisitor); + } + + public function landing(){ + $data = [ + 'title' => 'Beranda', + 'page' => 'landing', + 'news' => News::whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()]) + ->where('category', '=', '0') + ->latest() + ->get(), + 'announcements' => News::whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()]) + ->where('category', '=', '1') + ->latest() + ->get(), + ]; + + return view('homepage.index', $data); + } + + public function search(Request $request){ + + $request->validate([ + 'search' => 'nullable|string|max:255|regex:/^[a-zA-Z0-9\s]+$/' + ], [ + 'search.string' => 'Pencarian harus berupa teks.', + 'search.max' => 'Pencarian tidak boleh lebih dari 255 karakter.', + 'search.regex' => 'Pencarian hanya boleh mengandung huruf, angka, dan spasi.' + ]); + + $search = trim($request->input('search')); + + $data = [ + 'title' => 'Pencarian', + 'page' => 'search', + 'data' => News::query() + ->when($search, function ($query, $search) { + return $query->where('title', 'LIKE', '%' . $search . '%'); + }) + ->latest() + ->simplePaginate(12) + ->appends(['search' => $search]), + 'search' => $search + ]; + + return view('homepage.search', $data); + } + + public function news(Request $request){ + + $data = [ + 'title' => 'Berita', + 'page' => 'news', + ]; + + $search = $request->input('search', null); + + if($search){ + $data['news'] = News::where('title', 'LIKE', "%{$search}%") + ->where('category', '0') + ->latest() + ->simplePaginate(12); + }else{ + $data['news'] = News::where('category', '0')->latest()->simplePaginate(12); + } + + return view('homepage.news.index', $data); + } + + public function readNews($slug){ + $news = News::where('slug', $slug)->first(); + $news->increment('views', 1); + + $data = [ + 'title' => 'Berita', + 'page' => 'news', + 'news' => $news, + 'popularNews' => News::where('category', '0')->orderBy('views', 'desc')->take(5)->get(), + 'otherNews' => News::where('category', '0')->orderByDesc('id')->where('id', '!=', $news->id)->take(3)->get() + ]; + + return view('homepage.news.read', $data); + + } + + public function announcement(Request $request){ + + $data = [ + 'title' => 'Pengumuman', + 'page' => 'announcements', + ]; + + $search = $request->input('search', null); + + if($search){ + $data['announcements'] = News::where('category', '1')->where('title', 'LIKE', "%{$search}%") + // ->orWhere('content', 'LIKE', "%{$search}%") + ->latest() + ->simplePaginate(6); + }else{ + $data['announcements'] = News::where('category', '1')->latest()->simplePaginate(6); + } + + return view('homepage.announcements.index', $data); + } + + public function readAnnouncement($slug){ + $announcement = News::where('slug', $slug)->first(); + $announcement->increment('views', 1); + + $data = [ + 'title' => 'Pengumuman', + 'page' => 'announcements', + 'announcement' => $announcement, + 'otherAnnouncements' => News::where('category', '1')->orderByDesc('id')->where('id', '!=', $announcement->id)->take(4)->get() + ]; + + return view('homepage.announcements.read', $data); + + } + + public function aboutUs(){ + $data = [ + 'title' => 'Tentang Kami', + 'page' => 'about_us' + ]; + + return view('homepage.about-us.index', $data); + } + + public function contact(){ + $data = [ + 'title' => 'Kontak', + 'page' => 'contacts' + ]; + + return view('homepage.contacts.index', $data); + } +} diff --git a/app/Http/Middleware/VisitorCounterMiddleware.php b/app/Http/Middleware/VisitorCounterMiddleware.php new file mode 100644 index 0000000..220b0c6 --- /dev/null +++ b/app/Http/Middleware/VisitorCounterMiddleware.php @@ -0,0 +1,34 @@ +ip(); + + if (! RateLimiter::tooManyAttempts('visitor:'.$ipAddress, 1)) { + RateLimiter::hit('visitor:'.$ipAddress, 900); + + $today = now()->format('Y-m-d'); + + $visitor = Visitor::firstOrCreate(['date' => $today]); + $visitor->increment('visitors'); + } + + return $next($request); + } +} diff --git a/app/Models/Visitor.php b/app/Models/Visitor.php new file mode 100644 index 0000000..e44a098 --- /dev/null +++ b/app/Models/Visitor.php @@ -0,0 +1,16 @@ + RoleMiddleware::class, 'authenticated' => AuthenticatedMiddleware::class, 'announcement' => AnnouncementMiddleware::class, + 'visitor.count' => VisitorCounterMiddleware::class, ]); }) ->withExceptions(function (Exceptions $exceptions) { diff --git a/database/migrations/2025_03_30_160236_create_visitors_table.php b/database/migrations/2025_03_30_160236_create_visitors_table.php new file mode 100644 index 0000000..1c1e188 --- /dev/null +++ b/database/migrations/2025_03_30_160236_create_visitors_table.php @@ -0,0 +1,29 @@ +id(); + $table->date('date'); + $table->integer('visitors')->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('visitors'); + } +}; diff --git a/database/seeders/VisitorSeeder.php b/database/seeders/VisitorSeeder.php new file mode 100644 index 0000000..e72290c --- /dev/null +++ b/database/seeders/VisitorSeeder.php @@ -0,0 +1,17 @@ + Statistik Pengunjung diff --git a/routes/web.php b/routes/web.php index cdbb09a..29b3d4f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -25,33 +25,29 @@ use App\Http\Controllers\Dashboard\SubClassificationController; use App\Http\Controllers\Dashboard\SubLocationController; use App\Http\Controllers\Dashboard\UserController; -use App\Http\Controllers\Homepage\AboutUsController; -use App\Http\Controllers\Homepage\ContactController; -use App\Http\Controllers\Homepage\NewsController as HomepageNewsController; -use App\Http\Controllers\Homepage\AnnouncementController as HomepageAnnouncementController; -use App\Http\Controllers\Homepage\LandingPageController; +use App\Http\Controllers\HomepageController; use Illuminate\Support\Facades\Route; -Route::name('homepage.')->group(function(){ - Route::get('/', [LandingPageController::class, 'index'])->name('landing'); - Route::get('/search', [LandingPageController::class, 'search'])->name('search'); +Route::name('homepage.')->controller(HomepageController::class)->middleware('visitor.count')->group(function(){ + Route::get('/', 'landing')->name('landing'); + Route::get('/search', 'search')->name('search'); - Route::prefix('news')->name('news.')->controller(HomepageNewsController::class)->group(function(){ - Route::get('/', 'index')->name('index'); - Route::get('/{slug}', 'read')->name('read'); + Route::prefix('news')->name('news.')->group(function(){ + Route::get('/', 'news')->name('index'); + Route::get('/{slug}', 'readNews')->name('read'); }); - Route::prefix('announcements')->name('announcements.')->controller(HomepageAnnouncementController::class)->group(function(){ - Route::get('/', 'index')->name('index'); - Route::get('/{slug}', 'read')->name('read'); + Route::prefix('announcements')->name('announcements.')->group(function(){ + Route::get('/', 'announcement')->name('index'); + Route::get('/{slug}', 'readAnnouncement')->name('read'); }); - Route::prefix('about-us')->name('about.us.')->controller(AboutUsController::class)->group(function(){ - Route::get('/', 'index')->name('index'); + Route::prefix('about-us')->name('about.us.')->group(function(){ + Route::get('/', 'aboutUs')->name('index'); }); - Route::prefix('contacts')->name('contacts.')->controller(ContactController::class)->group(function(){ - Route::get('/', 'index')->name('index'); + Route::prefix('contacts')->name('contacts.')->group(function(){ + Route::get('/', 'contact')->name('index'); }); });