Component Details

Deep dive into the configuration and assets associated with this component.

Basic Information
Name api-graphics
Display Name API Graphics
Version 1.0.0
Status Active
Downloads 89
Description Component: API Graphics
Created 2025-11-11 20:42:10
Controller Code
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ApiGraphicsController extends Controller
{
    /**
     * Display the API graphics interface.
     */
    public function index(Request $request)
    {
        // Get Matomo configuration from authenticated user
        $user = auth()->user();
        
        if (!$user || !$user->matomo_configured) {
            return redirect()->route('onboarding')->with('error', 'Please configure your Matomo instance first.');
        }
        
        // Use global Matomo configuration from user profile
        $matomoUrl = $user->matomo_url;
        $matomoToken = $user->matomo_token ? decrypt($user->matomo_token) : null;
        $siteId = $user->matomo_site_id ?? 1;
        
        // Get dynamic parameters from URL (period, date, segment)
        $period = $request->get('period', 'day');
        $date = $request->get('date', 'today');
        $segment = $request->get('segment', '');
        // Format is always JSON for using tools directly
        $format = 'json';

        // Fetch Matomo data server-side to avoid Cloudflare blocking
        $matomoData = $this->fetchMatomoDataWithConfig($matomoUrl, $matomoToken, $siteId, $period, $date, $segment, $format);

        return view('components.api-graphics.index', compact('matomoData', 'matomoUrl', 'matomoToken', 'siteId', 'period', 'date', 'segment'));
    }
    
    private function fetchMatomoDataWithConfig($matomoUrl, $token, $siteId, $period, $date, $segment = '', $format = 'json')
    {
        $baseUrl = rtrim($matomoUrl, '/') . '/index.php';
        
        try {
            // Initialize data structure
            $data = [
                'visits' => [],
                'pageviews' => [],
                'countries' => [],
                'countryVisits' => [],
                'devices' => [],
                'deviceVisits' => [],
                'referrers' => [],
                'referrerVisits' => [],
                'goals' => [],
                'goalConversions' => [],
                'topPages' => [],
                'pageVisits' => []
            ];
            
            $context = stream_context_create([
                'http' => [
                    'timeout' => 30,
                    'method' => 'GET',
                    'header' => 'User-Agent: Laravel Matomo Dashboard'
                ]
            ]);

            // Build base parameters
            $baseParams = [
                'module' => 'API',
                'idSite' => $siteId,
                'period' => $period,
                'date' => $date,
                'format' => $format,
                'token_auth' => $token,
            ];

            if ($segment && $segment !== '' && $segment !== 'undefined') {
                $baseParams['segment'] = $segment;
            }

            // Fetch visits summary
            $visitsSummaryUrl = $baseUrl . '?' . http_build_query(array_merge($baseParams, ['method' => 'VisitsSummary.get']));
            
            $response = file_get_contents($visitsSummaryUrl, false, $context);
            if ($response !== false) {
                $summary = json_decode($response, true);
                if ($summary) {
                    $data['visits_summary'] = $summary;
                    \Illuminate\Support\Facades\Log::info('Matomo visits summary fetched:', $summary);
                } else {
                    \Illuminate\Support\Facades\Log::warning('Failed to decode visits summary response:', ['response' => $response]);
                }
            } else {
                \Illuminate\Support\Facades\Log::error('Failed to fetch visits summary from Matomo API');
            }
            
            // Fetch visits over time - use VisitsSummary.get for historical data
            // For range periods, get last 30 days
            $visitsDate = $date;
            if ($period === 'day' && $date === 'today') {
                // For today, get last 30 days
                $visitsDate = 'last30';
            } elseif ($period === 'day' && $date === 'yesterday') {
                // For yesterday, get last 30 days ending yesterday
                $yesterday = date('Y-m-d', strtotime('-1 day'));
                $visitsDate = date('Y-m-d', strtotime('-30 days')) . ',' . $yesterday;
            } elseif ($period === 'range') {
                // For range, use the range
                $visitsDate = $date;
            } else {
                // For other periods, get last 30 days
                $visitsDate = 'last30';
            }
            
            // Use VisitsSummary.get which returns data indexed by date
            $visitsOverTimeParams = array_merge($baseParams, [
                'method' => 'VisitsSummary.get',
                'date' => $visitsDate
            ]);
            
            $visitsOverTimeUrl = $baseUrl . '?' . http_build_query($visitsOverTimeParams);
            
            $response = @file_get_contents($visitsOverTimeUrl, false, $context);
            if ($response !== false) {
                $visitsData = json_decode($response, true);
                
                if ($visitsData && (!isset($visitsData['result']) || $visitsData['result'] !== 'error')) {
                    // Handle the response format: dates as keys, each value is an array with nb_visits, nb_actions, etc.
                    if (is_array($visitsData)) {
                        // Extract labels (dates) and visit counts
                        $labels = array_keys($visitsData);
                        $visits = array_map(function($v) {
                            return $v['nb_visits'] ?? 0;
                        }, $visitsData);
                        $pageviews = array_map(function($v) {
                            return $v['nb_actions'] ?? 0;
                        }, $visitsData);
                        
                        $data['visits'] = array_values($visits);
                        $data['pageviews'] = array_values($pageviews);
                        
                        \Illuminate\Support\Facades\Log::info('Visits over time fetched:', [
                            'count' => count($data['visits']),
                            'first_few' => array_slice($data['visits'], 0, 5)
                        ]);
                    } elseif (isset($visitsData['nb_visits'])) {
                        // Single day data - get historical data instead
                        $historicalParams = array_merge($baseParams, [
                            'method' => 'VisitsSummary.get',
                            'date' => 'last30'
                        ]);
                        $historicalUrl = $baseUrl . '?' . http_build_query($historicalParams);
                        $historicalResponse = @file_get_contents($historicalUrl, false, $context);
                        if ($historicalResponse !== false) {
                            $historicalData = json_decode($historicalResponse, true);
                            if (is_array($historicalData)) {
                                $visits = array_map(function($v) {
                                    return $v['nb_visits'] ?? 0;
                                }, $historicalData);
                                $pageviews = array_map(function($v) {
                                    return $v['nb_actions'] ?? 0;
                                }, $historicalData);
                                
                                $data['visits'] = array_values($visits);
                                $data['pageviews'] = array_values($pageviews);
                            }
                        }
                    }
                }
            }
            
            // Fetch countries data
            $countriesUrl = $baseUrl . '?' . http_build_query(array_merge($baseParams, ['method' => 'UserCountry.getCountry']));
            
            $response = file_get_contents($countriesUrl, false, $context);
            if ($response !== false) {
                $countriesData = json_decode($response, true);
                if (is_array($countriesData)) {
                    $countriesData = array_slice($countriesData, 0, 10);
                    foreach ($countriesData as $country) {
                        $data['countries'][] = $country['label'] ?? 'Unknown';
                        $data['countryVisits'][] = $country['nb_visits'] ?? 0;
                    }
                }
            }
            
            // Fetch top pages data
            $topPagesUrl = $baseUrl . '?' . http_build_query(array_merge($baseParams, ['method' => 'Actions.getPageUrls']));
            
            $response = file_get_contents($topPagesUrl, false, $context);
            if ($response !== false) {
                $pagesData = json_decode($response, true);
                if (is_array($pagesData)) {
                    $pagesData = array_slice($pagesData, 0, 10);
                    foreach ($pagesData as $page) {
                        $data['topPages'][] = $page['label'] ?? 'Unknown Page';
                        $data['pageVisits'][] = $page['nb_visits'] ?? 0;
                    }
                }
            }
            
            // Fetch devices data
            $devicesUrl = $baseUrl . '?' . http_build_query(array_merge($baseParams, ['method' => 'DevicesDetection.getType']));
            
            $response = file_get_contents($devicesUrl, false, $context);
            if ($response !== false) {
                $devicesData = json_decode($response, true);
                if (is_array($devicesData)) {
                    foreach ($devicesData as $device) {
                        $data['devices'][] = $device['label'] ?? 'Unknown';
                        $data['deviceVisits'][] = $device['nb_visits'] ?? 0;
                    }
                }
            }
            
            // Fetch referrers data
            $referrersUrl = $baseUrl . '?' . http_build_query(array_merge($baseParams, ['method' => 'Referrers.getReferrerType']));
            
            $response = file_get_contents($referrersUrl, false, $context);
            if ($response !== false) {
                $referrersData = json_decode($response, true);
                if (is_array($referrersData)) {
                    $referrersData = array_slice($referrersData, 0, 10);
                    foreach ($referrersData as $referrer) {
                        $data['referrers'][] = $referrer['label'] ?? 'Unknown';
                        $data['referrerVisits'][] = $referrer['nb_visits'] ?? 0;
                    }
                }
            }
            
            // Fetch goals data
            $goalsUrl = $baseUrl . '?' . http_build_query(array_merge($baseParams, ['method' => 'Goals.get']));
            
            $response = file_get_contents($goalsUrl, false, $context);
            if ($response !== false) {
                $goalsData = json_decode($response, true);
                if (is_array($goalsData)) {
                    foreach ($goalsData as $goal) {
                        $data['goals'][] = $goal['label'] ?? 'Unknown Goal';
                        $data['goalConversions'][] = $goal['nb_conversions'] ?? 0;
                    }
                }
            }
            
            // Ensure visits_summary is always set
            if (!isset($data['visits_summary'])) {
                $data['visits_summary'] = null;
            }
            
            return $data;
            
        } catch (\Exception $e) {
            \Illuminate\Support\Facades\Log::error('Matomo API Error: ' . $e->getMessage());
            return [
                'visits' => [],
                'pageviews' => [],
                'countries' => [],
                'countryVisits' => [],
                'devices' => [],
                'deviceVisits' => [],
                'referrers' => [],
                'referrerVisits' => [],
                'goals' => [],
                'goalConversions' => [],
                'topPages' => [],
                'pageVisits' => [],
                'visits_summary' => null,
                'error' => $e->getMessage()
            ];
        }
    }
}

Routes Code
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ApiGraphicsController;

Route::middleware(['auth', 'component.visible'])->group(function () {
    Route::get('/api-graphics', [ApiGraphicsController::class, 'index'])->name('dashboard.api-graphics');
});

Views (1)
  • index.blade.php Length: 191829 chars