Your IP : 216.73.216.79


Current Path : /var/www/html/redd/app/Models/Backend/
Upload File :
Current File : /var/www/html/redd/app/Models/Backend/Visit.php

<?php

namespace App\Models\Backend;

use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

#[Table('visits')]
#[Fillable([
    'method', 'request', 'url', 'referer',
    'languages', 'useragent', 'headers',
    'device', 'platform', 'browser', 'ip',
    'visitor_id', 'visitor_type', 'visitable_id', 'visitable_type', 'geo_raw',
])]
class Visit extends Model
{
    protected function casts(): array
    {
        return [
            'request'   => 'array',
            'languages' => 'array',
            'headers'   => 'array',
            'geo_raw'   => 'array',
        ];
    }

    // ──────────────────────────────────────
    //  RELATIONS
    // ──────────────────────────────────────

    public function visitable(): MorphTo
    {
        return $this->morphTo('visitable');
    }

    public function visitor(): MorphTo
    {
        return $this->morphTo('visitor');
    }

    // ──────────────────────────────────────
    //  LOGGING
    // ──────────────────────────────────────

    public static function log(?Request $request = null, ?Model $visitor = null, ?Model $visitable = null): ?self
    {
        $request ??= request();

        if (!$request) {
            return null;
        }

        $ua = $request->userAgent() ?? '';

        $data = [
            'method'     => $request->method(),
            'request'    => $request->all(),
            'url'        => $request->fullUrl(),
            'referer'    => $request->header('referer'),
            'languages'  => $request->getLanguages() ?? [],
            'useragent'  => $ua,
            'headers'    => $request->headers->all(),
            'device'     => self::detectDevice($ua),
            'platform'   => self::detectPlatform($ua),
            'browser'    => self::detectBrowser($ua),
            'ip'         => $request->ip(),
            'visitor_id'   => $visitor?->id ?? Auth::id(),
            'visitor_type' => $visitor?->getMorphClass() ?? (Auth::user()?->getMorphClass()),
        ];

        if ($visitable) {
            $data['visitable_id'] = $visitable->id;
            $data['visitable_type'] = $visitable->getMorphClass();
        }

        return self::create($data);
    }

    // ──────────────────────────────────────
    //  STATS (static)
    // ──────────────────────────────────────

    public static function totalVisits(): int
    {
        return self::count();
    }

    public static function uniqueIps(): int
    {
        return self::distinct('ip')->count('ip');
    }

    public static function todayVisits(): int
    {
        return self::whereDate('created_at', today())->count();
    }

    public static function yesterdayVisits(): int
    {
        return self::whereDate('created_at', today()->subDay())->count();
    }

    public static function thisWeekVisits(): int
    {
        return self::whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])->count();
    }

    public static function thisMonthVisits(): int
    {
        return self::whereMonth('created_at', now()->month)
            ->whereYear('created_at', now()->year)
            ->count();
    }

    public static function last3MonthsVisits(): int
    {
        return self::where('created_at', '>=', now()->subMonths(3))->count();
    }

    public static function last6MonthsVisits(): int
    {
        return self::where('created_at', '>=', now()->subMonths(6))->count();
    }

    public static function thisYearVisits(): int
    {
        return self::whereYear('created_at', now()->year)->count();
    }

    public static function lastYearVisits(): int
    {
        return self::where('created_at', '>=', now()->subYear())->count();
    }

    public static function uniqueVisitors(): int
    {
        return self::whereNotNull('visitor_id')
            ->distinct('visitor_id')
            ->count('visitor_id');
    }

    // ──────────────────────────────────────
    //  SCOPES
    // ──────────────────────────────────────

    public function scopeToday(Builder $q): Builder
    {
        return $q->whereDate('created_at', today());
    }

    public function scopeYesterday(Builder $q): Builder
    {
        return $q->whereDate('created_at', today()->subDay());
    }

    public function scopeThisWeek(Builder $q): Builder
    {
        return $q->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()]);
    }

    public function scopeThisMonth(Builder $q): Builder
    {
        return $q->whereMonth('created_at', now()->month)
            ->whereYear('created_at', now()->year);
    }

    public function scopeLast3Months(Builder $q): Builder
    {
        return $q->where('created_at', '>=', now()->subMonths(3));
    }

    public function scopeLast6Months(Builder $q): Builder
    {
        return $q->where('created_at', '>=', now()->subMonths(6));
    }

    public function scopeThisYear(Builder $q): Builder
    {
        return $q->whereYear('created_at', now()->year);
    }

    public function scopeLastYear(Builder $q): Builder
    {
        return $q->where('created_at', '>=', now()->subYear());
    }

    public function scopeByVisitor(Builder $q, Model $visitor): Builder
    {
        return $q->where('visitor_type', $visitor->getMorphClass())
            ->where('visitor_id', $visitor->id);
    }

    public function scopeByVisitable(Builder $q, Model $visitable): Builder
    {
        return $q->where('visitable_type', $visitable->getMorphClass())
            ->where('visitable_id', $visitable->id);
    }

    public function scopeByDevice(Builder $q, string $device): Builder
    {
        return $q->where('device', $device);
    }

    public function scopeByBrowser(Builder $q, string $browser): Builder
    {
        return $q->where('browser', $browser);
    }

    public function scopeByPlatform(Builder $q, string $platform): Builder
    {
        return $q->where('platform', $platform);
    }

    public function scopeByIp(Builder $q, string $ip): Builder
    {
        return $q->where('ip', $ip);
    }

    public function scopeByMethod(Builder $q, string $method): Builder
    {
        return $q->where('method', strtoupper($method));
    }

    public function scopeBetween(Builder $q, $start, $end): Builder
    {
        return $q->whereBetween('created_at', [$start, $end]);
    }

    public function scopeOnline(Builder $q, int $seconds = 180): Builder
    {
        return $q->where('created_at', '>=', now()->subSeconds($seconds));
    }

    // ──────────────────────────────────────
    //  USER AGENT DETECTION
    // ──────────────────────────────────────

    private static function detectDevice(string $ua): string
    {
        if (preg_match('/android/i', $ua)) return 'Android';
        if (preg_match('/(ipad|tablet|playbook|silk)|(android(?!.*mobile))/i', $ua)) return 'Tablet';
        if (preg_match('/(mobile|iphone|ipod|blackberry|opera mini|iemobile)/i', $ua)) return 'Mobile';
        return 'Desktop';
    }

    private static function detectPlatform(string $ua): string
    {
        if (preg_match('/windows nt/i', $ua)) return 'Windows';
        if (preg_match('/macintosh|mac os x/i', $ua)) return 'macOS';
        if (preg_match('/linux/i', $ua)) return 'Linux';
        if (preg_match('/android/i', $ua)) return 'Android';
        if (preg_match('/iphone|ipad|ipod/i', $ua)) return 'iOS';
        return 'Unknown';
    }

    private static function detectBrowser(string $ua): string
    {
        if (preg_match('/edg/i', $ua)) return 'Edge';
        if (preg_match('/chrome/i', $ua)) return 'Chrome';
        if (preg_match('/safari/i', $ua)) return 'Safari';
        if (preg_match('/firefox/i', $ua)) return 'Firefox';
        if (preg_match('/opera|opr/i', $ua)) return 'Opera';
        if (preg_match('/msie|trident/i', $ua)) return 'IE';
        return 'Unknown';
    }
}