Analytics - v1.0.0-beta1
Contracts
ArtisanPack UI Analytics provides interfaces (contracts) that allow you to create custom implementations.
AnalyticsProviderInterface
Interface for analytics providers.
Definition
namespace ArtisanPackUI\Analytics\Contracts;
use ArtisanPackUI\Analytics\Data\EventData;
use ArtisanPackUI\Analytics\Data\PageViewData;
interface AnalyticsProviderInterface
{
/**
* Track a page view.
*/
public function trackPageView(PageViewData $data): void;
/**
* Track a custom event.
*/
public function trackEvent(EventData $data): void;
/**
* Check if the provider is enabled.
*/
public function isEnabled(): bool;
/**
* Get the provider name.
*/
public function getName(): string;
}
Implementation Example
use ArtisanPackUI\Analytics\Contracts\AnalyticsProviderInterface;
use ArtisanPackUI\Analytics\Data\EventData;
use ArtisanPackUI\Analytics\Data\PageViewData;
class MixpanelProvider implements AnalyticsProviderInterface
{
public function __construct(
private MixpanelClient $client,
) {}
public function trackPageView(PageViewData $data): void
{
$this->client->track('Page View', [
'path' => $data->path,
'title' => $data->title,
]);
}
public function trackEvent(EventData $data): void
{
$this->client->track($data->name, $data->properties ?? []);
}
public function isEnabled(): bool
{
return config('services.mixpanel.enabled', false);
}
public function getName(): string
{
return 'mixpanel';
}
}
Registering Custom Providers
// In a service provider
use ArtisanPackUI\Analytics\Facades\Analytics;
public function boot(): void
{
Analytics::extend('mixpanel', function ($app) {
return new MixpanelProvider(
$app->make(MixpanelClient::class)
);
});
}
Then enable in config:
// config/artisanpack/analytics.php
'active_providers' => ['local', 'mixpanel'],
SiteResolverInterface
Interface for resolving the current site in multi-tenant setups.
Definition
namespace ArtisanPackUI\Analytics\Contracts;
use ArtisanPackUI\Analytics\Models\Site;
use Illuminate\Http\Request;
interface SiteResolverInterface
{
/**
* Resolve the site from the request.
*/
public function resolve(Request $request): ?Site;
/**
* Get the resolver priority (lower runs first).
*/
public function getPriority(): int;
}
Built-in Resolvers
| Resolver | Priority | Resolution Method |
|---|---|---|
ApiKeyResolver |
10 | API key in header or query |
HeaderResolver |
50 | Custom header (X-Site-ID) |
SubdomainResolver |
90 | Subdomain extraction |
DomainResolver |
100 | Full domain matching |
Implementation Example
use ArtisanPackUI\Analytics\Contracts\SiteResolverInterface;
use ArtisanPackUI\Analytics\Models\Site;
use Illuminate\Http\Request;
class TenantIdResolver implements SiteResolverInterface
{
public function resolve(Request $request): ?Site
{
// Get tenant from authenticated user
$user = $request->user();
if (!$user || !$user->tenant_id) {
return null;
}
return Site::where('tenant_id', $user->tenant_id)->first();
}
public function getPriority(): int
{
return 20; // Run early, after API key
}
}
Registering Custom Resolvers
// config/artisanpack/analytics.php
'multi_tenant' => [
'resolvers' => [
\ArtisanPackUI\Analytics\Resolvers\ApiKeyResolver::class,
\App\Analytics\TenantIdResolver::class, // Your custom resolver
\ArtisanPackUI\Analytics\Resolvers\DomainResolver::class,
],
],
TenantResolverInterface
Legacy interface for tenant resolution.
Definition
namespace ArtisanPackUI\Analytics\Contracts;
interface TenantResolverInterface
{
/**
* Get the current tenant ID.
*/
public function getCurrentTenantId(): int|string|null;
/**
* Set the current tenant ID.
*/
public function setCurrentTenantId(int|string $tenantId): void;
}
Implementation Example
use ArtisanPackUI\Analytics\Contracts\TenantResolverInterface;
class SessionTenantResolver implements TenantResolverInterface
{
public function getCurrentTenantId(): int|string|null
{
return session('current_tenant_id');
}
public function setCurrentTenantId(int|string $tenantId): void
{
session(['current_tenant_id' => $tenantId]);
}
}
Registration
// config/artisanpack/analytics.php
'multi_tenant' => [
'resolver' => \App\Analytics\SessionTenantResolver::class,
],
Creating Custom Contracts
You can extend the package with your own contracts:
namespace App\Analytics\Contracts;
use ArtisanPackUI\Analytics\Models\PageView;
interface PageViewEnricherInterface
{
/**
* Enrich a page view with additional data.
*/
public function enrich(PageView $pageView): PageView;
}
Implementation:
class GeoEnricher implements PageViewEnricherInterface
{
public function __construct(
private GeoIpService $geoService,
) {}
public function enrich(PageView $pageView): PageView
{
$location = $this->geoService->lookup($pageView->ip_address);
$pageView->custom_data = array_merge(
$pageView->custom_data ?? [],
['geo' => $location]
);
return $pageView;
}
}
Register in service provider:
$this->app->bind(
PageViewEnricherInterface::class,
GeoEnricher::class
);
Dependency Injection
All contracts can be injected into your classes:
class MyService
{
public function __construct(
private AnalyticsProviderInterface $provider,
private SiteResolverInterface $resolver,
) {}
public function doSomething(): void
{
if ($this->provider->isEnabled()) {
// Use provider
}
}
}
Testing with Contracts
Use contracts for easy mocking in tests:
use ArtisanPackUI\Analytics\Contracts\AnalyticsProviderInterface;
test('tracks page view', function () {
$provider = Mockery::mock(AnalyticsProviderInterface::class);
$provider->shouldReceive('trackPageView')->once();
app()->instance(AnalyticsProviderInterface::class, $provider);
// Test code that triggers page view tracking
});