feat: visitor counter

This commit is contained in:
myuqinurrohman 2025-03-31 14:40:05 +07:00
parent 7cd72f7f0f
commit 497deeef18
13 changed files with 304 additions and 208 deletions

View File

@ -1,18 +0,0 @@
<?php
namespace App\Http\Controllers\Homepage;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AboutUsController extends Controller
{
public function index(){
$data = [
'title' => 'Tentang Kami',
'page' => 'about_us'
];
return view('homepage.about-us.index', $data);
}
}

View File

@ -1,46 +0,0 @@
<?php
namespace App\Http\Controllers\Homepage;
use App\Http\Controllers\Controller;
use App\Models\News;
use Illuminate\Http\Request;
class AnnouncementController extends Controller
{
public function index(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 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);
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace App\Http\Controllers\Homepage;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ContactController extends Controller
{
public function index(){
$data = [
'title' => 'Kontak',
'page' => 'contacts'
];
return view('homepage.contacts.index', $data);
}
}

View File

@ -1,57 +0,0 @@
<?php
namespace App\Http\Controllers\Homepage;
use App\Http\Controllers\Controller;
use App\Models\News;
use Carbon\Carbon;
use Illuminate\Http\Request;
class LandingPageController extends Controller
{
public function index(){
$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);
}
}

View File

@ -1,48 +0,0 @@
<?php
namespace App\Http\Controllers\Homepage;
use App\Http\Controllers\Controller;
use App\Models\News;
use Illuminate\Http\Request;
class NewsController extends Controller
{
public function index(Request $request){
$data = [
'title' => '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);
}
}

View File

@ -0,0 +1,189 @@
<?php
namespace App\Http\Controllers;
use App\Models\News;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\View;
class HomepageController extends Controller
{
protected $yesterdayVisitor;
protected $todayVisitor;
protected $thisMonthVisitor;
protected $thisYearVisitor;
protected $totalVisitor;
public function __construct(){
$today = Carbon::today();
$yesterday = Carbon::yesterday();
$thisMonth = Carbon::now()->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);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Http\Middleware;
use App\Models\Visitor;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\RateLimiter;
use Symfony\Component\HttpFoundation\Response;
class VisitorCounterMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$ipAddress = $request->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);
}
}

16
app/Models/Visitor.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Visitor extends Model
{
protected $table = 'visitors';
protected $fillable = [
'date',
'visitors'
];
}

View File

@ -3,6 +3,7 @@
use App\Http\Middleware\AnnouncementMiddleware;
use App\Http\Middleware\AuthenticatedMiddleware;
use App\Http\Middleware\RoleMiddleware;
use App\Http\Middleware\VisitorCounterMiddleware;
use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
@ -22,6 +23,7 @@
'role' => RoleMiddleware::class,
'authenticated' => AuthenticatedMiddleware::class,
'announcement' => AnnouncementMiddleware::class,
'visitor.count' => VisitorCounterMiddleware::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {

View File

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('visitors', function (Blueprint $table) {
$table->id();
$table->date('date');
$table->integer('visitors')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('visitors');
}
};

View File

@ -0,0 +1,17 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class VisitorSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
}
}

View File

@ -23,9 +23,9 @@
<div class="col-6 col-lg-3 col-sm-4 xs-mb-30px order-sm-3 order-lg-2">
<span class="fs-17 fw-500 d-block text-white mb-5px">Statistik Pengunjung</span>
<ul>
<li>Hari ini: 100</li>
<li>Bulan ini: 1000</li>
<li>Total: 10000</li>
<li>Hari ini: {{$todayVisitor}}</li>
<li>Bulan ini: {{$thisMonthVisitor}}</li>
<li>Total: {{$totalVisitor}}</li>
</ul>
</div>
<!-- end footer column -->

View File

@ -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');
});
});