Analytics - v1.0.0-beta1
Caching
ArtisanPack UI Analytics uses caching to optimize performance for dashboard queries and real-time data.
Configuration
Dashboard Cache
// config/artisanpack/analytics.php
'dashboard' => [
'cache_duration' => env('ANALYTICS_CACHE_DURATION', 300), // 5 minutes
],
Goals Cache
'goals' => [
'cache_duration' => env('ANALYTICS_GOALS_CACHE', 300),
],
Cache Keys
The package uses structured cache keys:
analytics:{site_id}:{query_type}:{date_range}:{hash}
Examples:
analytics:1:stats:2024-01-01_2024-01-31:abc123analytics:1:top_pages:2024-01-01_2024-01-31:def456
Using the DateRange for Cache Keys
use ArtisanPackUI\Analytics\Data\DateRange;
$range = DateRange::last30Days();
$cacheKey = 'my_query_' . $range->toKey();
// my_query_2024-01-01_2024-01-31
Manual Caching
In Custom Queries
use Illuminate\Support\Facades\Cache;
use ArtisanPackUI\Analytics\Data\DateRange;
$range = DateRange::last7Days();
$cacheKey = "custom_stats_{$range->toKey()}";
$cacheDuration = config('artisanpack.analytics.dashboard.cache_duration', 300);
$stats = Cache::remember($cacheKey, $cacheDuration, function () use ($range) {
return PageView::whereBetween('created_at', [
$range->startDate,
$range->endDate,
])->count();
});
In Livewire Components
use ArtisanPackUI\Analytics\Http\Livewire\Concerns\WithAnalyticsWidget;
class CustomWidget extends Component
{
use WithAnalyticsWidget;
public function loadData(): void
{
$range = $this->getDateRange();
$cacheKey = $this->getCacheKey('custom_data');
$this->data = Cache::remember($cacheKey, 300, function () use ($range) {
return $this->getAnalyticsQuery()->getStats($range);
});
}
protected function getCacheKey(string $suffix): string
{
$range = $this->getDateRange();
$siteId = $this->siteId ?? 'global';
return "analytics:{$siteId}:{$suffix}:{$range->toKey()}";
}
}
Cache Invalidation
Clear All Analytics Cache
php artisan analytics:clear-cache
Programmatic Clearing
use Illuminate\Support\Facades\Cache;
// Clear specific key
Cache::forget('analytics:1:stats:2024-01-01_2024-01-31:abc123');
// Clear by tag (if using tagged cache)
Cache::tags(['analytics', 'site_1'])->flush();
After Data Changes
Events can trigger cache invalidation:
use ArtisanPackUI\Analytics\Events\PageViewRecorded;
Event::listen(PageViewRecorded::class, function ($event) {
// Clear real-time cache
Cache::forget("analytics:{$event->pageView->site_id}:realtime");
});
Cache Drivers
Recommended Drivers
For production, use:
- Redis - Best performance, supports tags
- Memcached - Good performance
- Database - When Redis/Memcached unavailable
// .env
CACHE_DRIVER=redis
Driver-Specific Configuration
// config/cache.php
'stores' => [
'analytics' => [
'driver' => 'redis',
'connection' => 'analytics',
],
],
Use a dedicated cache store:
Cache::store('analytics')->remember($key, $duration, $callback);
Cache Tags
If your cache driver supports tags:
use Illuminate\Support\Facades\Cache;
// Store with tags
Cache::tags(['analytics', 'site_1', 'stats'])
->remember($key, 300, $callback);
// Clear by tag
Cache::tags(['site_1'])->flush();
Cache::tags(['stats'])->flush();
Real-Time Data Caching
Real-time data uses shorter cache durations:
'dashboard' => [
'realtime_interval' => 30, // Refresh every 30 seconds
],
The real-time widget polls at this interval, and data is cached for the same duration.
Query Caching in AnalyticsQuery
The AnalyticsQuery service automatically caches results:
use ArtisanPackUI\Analytics\Services\AnalyticsQuery;
$query = app(AnalyticsQuery::class);
// This result is automatically cached
$stats = $query->getStats($range);
// Force fresh data (bypass cache)
$query->withoutCache();
$freshStats = $query->getStats($range);
Cache Warming
Pre-populate cache for better user experience:
// In a scheduled command
use ArtisanPackUI\Analytics\Services\AnalyticsQuery;
use ArtisanPackUI\Analytics\Data\DateRange;
use ArtisanPackUI\Analytics\Models\Site;
class WarmAnalyticsCache extends Command
{
protected $signature = 'analytics:warm-cache';
public function handle(AnalyticsQuery $query): void
{
$ranges = [
DateRange::today(),
DateRange::last7Days(),
DateRange::last30Days(),
];
Site::active()->each(function ($site) use ($query, $ranges) {
foreach ($ranges as $range) {
$query->forSite($site->id)->getStats($range);
$query->forSite($site->id)->getTopPages($range);
}
});
$this->info('Cache warmed successfully');
}
}
Schedule it:
// routes/console.php
Schedule::command('analytics:warm-cache')->hourly();
Performance Tips
1. Use Appropriate Cache Duration
// Frequently changing (real-time): 30 seconds
'realtime_interval' => 30,
// Dashboard stats: 5 minutes
'cache_duration' => 300,
// Historical reports: 1 hour
'historical_cache' => 3600,
2. Cache at the Right Level
// Bad: Caching raw database results
$rows = Cache::remember('all_pageviews', 300, function () {
return PageView::all(); // Too much data
});
// Good: Caching computed results
$stats = Cache::remember('pageview_count', 300, function () {
return PageView::count(); // Just the number
});
3. Use Cache Tags for Invalidation
// Store with meaningful tags
Cache::tags(['analytics', "site_{$siteId}", 'daily_stats'])
->put($key, $data, 300);
// Invalidate all site data on significant changes
Cache::tags(["site_{$siteId}"])->flush();
4. Consider Cache Stampede
Use locks for expensive queries:
$stats = Cache::lock("analytics_lock_{$key}", 10)->block(5, function () use ($key) {
return Cache::remember($key, 300, function () {
return $this->expensiveQuery();
});
});
Monitoring Cache Performance
// Log cache hits/misses
Cache::macro('rememberWithLogging', function ($key, $ttl, $callback) {
$start = microtime(true);
$hit = Cache::has($key);
$result = Cache::remember($key, $ttl, $callback);
Log::debug('Cache access', [
'key' => $key,
'hit' => $hit,
'duration' => microtime(true) - $start,
]);
return $result;
});