First commit
This commit is contained in:
14 files changed
+426
-139
No files matched your search
@@ -1,61 +1,80 @@
|
||||
# This is my package matomo-proxy-laravel
|
||||
# Matomo Proxy for Laravel
|
||||
|
||||
[](https://packagist.org/packages/mattitjaab/matomo-proxy-laravel)
|
||||
[](https://github.com/mattitjaab/matomo-proxy-laravel/actions?query=workflow%3Arun-tests+branch%3Amain)
|
||||
[](https://github.com/mattitjaab/matomo-proxy-laravel/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
|
||||
[](https://packagist.org/packages/mattitjaab/matomo-proxy-laravel)
|
||||
|
||||
This is where your description should go. Limit it to a paragraph or two. Consider adding a small example.
|
||||
|
||||
## Support us
|
||||
|
||||
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/matomo-proxy-laravel.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/matomo-proxy-laravel)
|
||||
|
||||
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
|
||||
|
||||
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
|
||||
This package provides a plug-and-play **server-side Matomo tracking middleware** for Laravel.
|
||||
Instead of embedding JavaScript trackers in your frontend, requests are tracked directly from your Laravel app to your Matomo instance, ensuring better privacy, ad-blocker resilience, and simplified integration.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the package via composer:
|
||||
Install the package via Composer:
|
||||
|
||||
```bash
|
||||
composer require mattitjaab/matomo-proxy-laravel
|
||||
```
|
||||
|
||||
You can publish and run the migrations with:
|
||||
|
||||
```bash
|
||||
php artisan vendor:publish --tag="matomo-proxy-laravel-migrations"
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
You can publish the config file with:
|
||||
Publish the config file:
|
||||
|
||||
```bash
|
||||
php artisan vendor:publish --tag="matomo-proxy-laravel-config"
|
||||
```
|
||||
|
||||
This is the contents of the published config file:
|
||||
This will create a `config/matomo-proxy-laravel.php` file. Example:
|
||||
|
||||
```php
|
||||
return [
|
||||
'enabled' => env('MATOMO_ENABLED', true),
|
||||
|
||||
// Full Matomo tracking endpoint (must include /matomo.php)
|
||||
'base_url' => env('MATOMO_BASE_URL', ''),
|
||||
|
||||
// Matomo site ID
|
||||
'site_id' => env('MATOMO_SITE_ID', null),
|
||||
|
||||
// Optional token_auth (can be null for anonymous tracking)
|
||||
'token' => env('MATOMO_TOKEN', null),
|
||||
|
||||
// Paths that should never be tracked
|
||||
'ignore' => [
|
||||
'telescope/*', 'horizon/*', 'health', '_debugbar/*',
|
||||
],
|
||||
|
||||
// HTTP client behavior
|
||||
'timeout' => env('MATOMO_TIMEOUT', 2),
|
||||
'retry' => [
|
||||
'times' => env('MATOMO_RETRY_TIMES', 0),
|
||||
'sleep' => env('MATOMO_RETRY_SLEEP_MS', 100), // milliseconds
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
Optionally, you can publish the views using
|
||||
|
||||
```bash
|
||||
php artisan vendor:publish --tag="matomo-proxy-laravel-views"
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Register the middleware globally in your `bootstrap/app.php`:
|
||||
|
||||
```php
|
||||
$matomoProxyLaravel = new MattitjaAB\MatomoProxyLaravel();
|
||||
echo $matomoProxyLaravel->echoPhrase('Hello, MattitjaAB!');
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
$middleware->append(\MattitjaAB\MatomoProxyLaravel\Middleware\MatomoProxyLaravelMiddleware::class);
|
||||
})
|
||||
```
|
||||
|
||||
2. Configure your `.env` file:
|
||||
|
||||
```dotenv
|
||||
MATOMO_ENABLED=true
|
||||
MATOMO_BASE_URL=https://analytics.example.com/matomo.php
|
||||
MATOMO_SITE_ID=1
|
||||
MATOMO_TOKEN=null
|
||||
MATOMO_TIMEOUT=2
|
||||
MATOMO_RETRY_TIMES=0
|
||||
MATOMO_RETRY_SLEEP_MS=100
|
||||
```
|
||||
|
||||
✅ That’s it. All requests will now be tracked server-side in Matomo without requiring any frontend JS snippet.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
@@ -66,18 +85,10 @@ composer test
|
||||
|
||||
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
|
||||
|
||||
## Credits
|
||||
|
||||
- [MattitjaAB](https://github.com/MattitjaAB)
|
||||
- [All Contributors](../../contributors)
|
||||
* [MattitjaAB](https://github.com/MattitjaAB)
|
||||
* [All Contributors](../../contributors)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+1
-4
@@ -63,10 +63,7 @@
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"MattitjaAB\\MatomoProxyLaravel\\MatomoProxyLaravelServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"MatomoProxyLaravel": "MattitjaAB\\MatomoProxyLaravel\\Facades\\MatomoProxyLaravel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
<?php
|
||||
|
||||
// config for MattitjaAB/MatomoProxyLaravel
|
||||
return [
|
||||
// Enable/disable tracking
|
||||
'enabled' => env('MATOMO_ENABLED', true),
|
||||
|
||||
// Full Matomo tracking endpoint (must include /matomo.php)
|
||||
// Example: https://analytics.example.com/matomo.php
|
||||
'base_url' => env('MATOMO_BASE_URL', ''),
|
||||
|
||||
// Matomo site ID
|
||||
'site_id' => env('MATOMO_SITE_ID', null),
|
||||
|
||||
// Optional token_auth (can be null for anonymous tracking)
|
||||
'token' => env('MATOMO_TOKEN', null),
|
||||
|
||||
// Paths that should never be tracked
|
||||
'ignore' => [
|
||||
'telescope/*', 'horizon/*', 'health', '_debugbar/*',
|
||||
],
|
||||
|
||||
// HTTP client behavior
|
||||
'timeout' => env('MATOMO_TIMEOUT', 2),
|
||||
'retry' => [
|
||||
'times' => env('MATOMO_RETRY_TIMES', 0),
|
||||
'sleep' => env('MATOMO_RETRY_SLEEP_MS', 100), // milliseconds
|
||||
],
|
||||
];
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel\Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/*
|
||||
class ModelFactory extends Factory
|
||||
{
|
||||
protected $model = YourModel::class;
|
||||
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('matomo_proxy_laravel_table', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
// add fields
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
};
|
||||
+1
-2
@@ -5,8 +5,7 @@ parameters:
|
||||
level: 5
|
||||
paths:
|
||||
- src
|
||||
- config
|
||||
- database
|
||||
tmpDir: build/phpstan
|
||||
checkOctaneCompatibility: true
|
||||
checkModelProperties: true
|
||||
|
||||
Whitespace-only changes.
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class MatomoProxyLaravelCommand extends Command
|
||||
{
|
||||
public $signature = 'matomo-proxy-laravel';
|
||||
|
||||
public $description = 'My command';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$this->comment('All done');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
/**
|
||||
* @see \MattitjaAB\MatomoProxyLaravel\MatomoProxyLaravel
|
||||
*/
|
||||
class MatomoProxyLaravel extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor(): string
|
||||
{
|
||||
return \MattitjaAB\MatomoProxyLaravel\MatomoProxyLaravel::class;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel;
|
||||
|
||||
class MatomoProxyLaravel {}
|
||||
@@ -2,24 +2,23 @@
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel;
|
||||
|
||||
use MattitjaAB\MatomoProxyLaravel\Commands\MatomoProxyLaravelCommand;
|
||||
use Illuminate\Contracts\Http\Kernel as HttpKernel;
|
||||
use MattitjaAB\MatomoProxyLaravel\Middleware\MatomoProxyLaravelMiddleware;
|
||||
use Spatie\LaravelPackageTools\Package;
|
||||
use Spatie\LaravelPackageTools\PackageServiceProvider;
|
||||
|
||||
class MatomoProxyLaravelServiceProvider extends PackageServiceProvider
|
||||
final class MatomoProxyLaravelServiceProvider extends PackageServiceProvider
|
||||
{
|
||||
public function configurePackage(Package $package): void
|
||||
{
|
||||
/*
|
||||
* This class is a Package Service Provider
|
||||
*
|
||||
* More info: https://github.com/spatie/laravel-package-tools
|
||||
*/
|
||||
$package
|
||||
->name('matomo-proxy-laravel')
|
||||
->hasConfigFile()
|
||||
->hasViews()
|
||||
->hasMigration('create_matomo_proxy_laravel_table')
|
||||
->hasCommand(MatomoProxyLaravelCommand::class);
|
||||
->hasConfigFile('matomo-proxy-laravel');
|
||||
}
|
||||
|
||||
public function packageBooted(): void
|
||||
{
|
||||
$this->app->make(HttpKernel::class)
|
||||
->pushMiddleware(MatomoProxyLaravelMiddleware::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class MatomoProxyLaravelMiddleware
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
/** @var Response $response */
|
||||
$response = $next($request);
|
||||
|
||||
// Only track GET requests
|
||||
if (! $request->isMethod('GET')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Global feature toggle + required config
|
||||
if (! $this->isEnabled()) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Only track HTML responses
|
||||
if (! $this->isHtmlResponse($response)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Respect ignore patterns
|
||||
if ($this->isIgnoredPath($request)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Skip non-success/redirect responses (e.g., 404, 5xx)
|
||||
if (! $this->isTrackableStatus($response)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Ensure endpoint is present
|
||||
$endpoint = $this->buildEndpoint();
|
||||
if ($endpoint === null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Build payload now; do not capture Request in any deferred closure
|
||||
$params = $this->buildParams($request);
|
||||
|
||||
// Send synchronously (you can swap to app()->terminating() if desired)
|
||||
$this->send($endpoint, $params);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enabled flag and required keys.
|
||||
*/
|
||||
private function isEnabled(): bool
|
||||
{
|
||||
return config('matomo-proxy-laravel.enabled') === true
|
||||
&& filled(config('matomo-proxy-laravel.base_url'))
|
||||
&& filled(config('matomo-proxy-laravel.site_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Only track HTML (also accept application/xhtml+xml).
|
||||
*/
|
||||
private function isHtmlResponse(Response $response): bool
|
||||
{
|
||||
$contentType = $response->headers->get('content-type');
|
||||
|
||||
return $contentType !== null
|
||||
&& (str_starts_with($contentType, 'text/html')
|
||||
|| str_starts_with($contentType, 'application/xhtml+xml'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Respect configurable ignore patterns.
|
||||
*/
|
||||
private function isIgnoredPath(Request $request): bool
|
||||
{
|
||||
foreach (config('matomo-proxy-laravel.ignore', []) as $pattern) {
|
||||
if ($request->is($pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count only successful (2xx) and redirect (3xx) responses.
|
||||
*/
|
||||
private function isTrackableStatus(Response $response): bool
|
||||
{
|
||||
$code = $response->getStatusCode();
|
||||
|
||||
return $code >= 200 && $code < 400;
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint must already include /matomo.php.
|
||||
*/
|
||||
private function buildEndpoint(): ?string
|
||||
{
|
||||
$endpoint = config('matomo-proxy-laravel.base_url');
|
||||
|
||||
return filled($endpoint) ? trim((string) $endpoint) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Matomo Tracking API payload.
|
||||
*/
|
||||
private function buildParams(Request $request): array
|
||||
{
|
||||
$params = [
|
||||
'idsite' => (string) config('matomo-proxy-laravel.site_id', ''),
|
||||
'rec' => 1,
|
||||
'apiv' => 1,
|
||||
'url' => $request->fullUrl(),
|
||||
'action_name' => $request->route()?->getName() ?: $request->path(),
|
||||
'ua' => $request->userAgent(),
|
||||
];
|
||||
|
||||
if ($ref = $request->headers->get('referer')) {
|
||||
$params['urlref'] = $ref;
|
||||
}
|
||||
|
||||
if ($lang = $request->headers->get('accept-language')) {
|
||||
$params['lang'] = $lang;
|
||||
}
|
||||
|
||||
if ($ip = $request->ip()) {
|
||||
$params['cip'] = $ip;
|
||||
}
|
||||
|
||||
if ($token = trim((string) config('matomo-proxy-laravel.token', ''))) {
|
||||
$params['token_auth'] = $token;
|
||||
}
|
||||
|
||||
if ($uid = $request->user()->id ?? null) {
|
||||
$params['uid'] = (string) $uid;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the HTTP request (synchronous).
|
||||
*/
|
||||
private function send(string $endpoint, array $params): void
|
||||
{
|
||||
$timeout = (int) config('matomo-proxy-laravel.timeout', 2);
|
||||
$retryTimes = (int) config('matomo-proxy-laravel.retry.times', 0);
|
||||
$retrySleep = (int) config('matomo-proxy-laravel.retry.sleep.ms', 100);
|
||||
|
||||
try {
|
||||
Http::asForm()
|
||||
->timeout($timeout)
|
||||
->retry($retryTimes, $retrySleep)
|
||||
->post($endpoint, $params);
|
||||
} catch (\Throwable) {
|
||||
// Never let analytics affect the response lifecycle
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
it('can test', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Http\Client\Request as HttpClientRequest;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use MattitjaAB\MatomoProxyLaravel\Middleware\MatomoProxyLaravelMiddleware;
|
||||
|
||||
it('sends a tracking hit and includes user id when authenticated', function () {
|
||||
config()->set('matomo-proxy-laravel.enabled', true);
|
||||
config()->set('matomo-proxy-laravel.base_url', 'https://matomo.example/matomo.php');
|
||||
config()->set('matomo-proxy-laravel.site_id', '1');
|
||||
config()->set('matomo-proxy-laravel.token', 'secret-token');
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
'https://matomo.example/matomo.php' => Http::response('OK', 200),
|
||||
]);
|
||||
|
||||
$user = new class implements \Illuminate\Contracts\Auth\Authenticatable
|
||||
{
|
||||
public int $id = 123;
|
||||
|
||||
public function getAuthIdentifierName()
|
||||
{
|
||||
return 'id';
|
||||
}
|
||||
|
||||
public function getAuthIdentifier()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getAuthPasswordName()
|
||||
{
|
||||
return 'password';
|
||||
}
|
||||
|
||||
public function getAuthPassword()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getRememberToken()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setRememberToken($value): void {}
|
||||
|
||||
public function getRememberTokenName()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
Route::get('/page', fn () => response('ok', 200, ['Content-Type' => 'text/html']))
|
||||
->name('test.route');
|
||||
|
||||
$this->be($user);
|
||||
|
||||
$this->get('/page', [
|
||||
'Accept-Language' => 'sv-SE',
|
||||
'User-Agent' => 'PHPUnit',
|
||||
'Referer' => 'https://ref.example',
|
||||
])->assertOk();
|
||||
|
||||
Http::assertSent(function (HttpClientRequest $request) {
|
||||
$params = $request->data();
|
||||
|
||||
return $request->url() === 'https://matomo.example/matomo.php'
|
||||
&& $request->method() === 'POST'
|
||||
&& ($params['idsite'] ?? null) === '1'
|
||||
&& ($params['uid'] ?? null) === '123'
|
||||
&& ($params['token_auth'] ?? null) === 'secret-token'
|
||||
&& ($params['action_name'] ?? null) === 'test.route'
|
||||
&& ($params['urlref'] ?? null) === 'https://ref.example'
|
||||
&& ($params['lang'] ?? null) === 'sv-SE'
|
||||
&& ($params['ua'] ?? null) === 'PHPUnit'
|
||||
&& ($params['rec'] ?? null) === 1
|
||||
&& ($params['apiv'] ?? null) === 1;
|
||||
});
|
||||
|
||||
Http::assertSentCount(1);
|
||||
});
|
||||
|
||||
it('does nothing when disabled', function () {
|
||||
config()->set('matomo-proxy-laravel.enabled', false);
|
||||
config()->set('matomo-proxy-laravel.base_url', 'https://matomo.example/matomo.php');
|
||||
config()->set('matomo-proxy-laravel.site_id', '1');
|
||||
|
||||
Http::fake();
|
||||
|
||||
Route::get('/disabled', fn () => 'ok')
|
||||
->middleware(MatomoProxyLaravelMiddleware::class);
|
||||
|
||||
$this->get('/disabled')->assertOk();
|
||||
|
||||
Http::assertNothingSent();
|
||||
});
|
||||
|
||||
it('never breaks the response when exceptions occur during tracking', function () {
|
||||
config()->set('matomo-proxy-laravel.enabled', true);
|
||||
config()->set('matomo-proxy-laravel.base_url', 'https://matomo.example/matomo.php');
|
||||
config()->set('matomo-proxy-laravel.site_id', '1');
|
||||
|
||||
Http::fake(function () {
|
||||
throw new Exception('network error');
|
||||
});
|
||||
|
||||
Route::get('/exception', fn () => 'ok')
|
||||
->middleware(MatomoProxyLaravelMiddleware::class);
|
||||
|
||||
$this->get('/exception')->assertOk();
|
||||
});
|
||||
|
||||
it('does not send tracking for non-get requests', function () {
|
||||
config()->set('matomo-proxy-laravel.enabled', true);
|
||||
config()->set('matomo-proxy-laravel.base_url', 'https://matomo.example/matomo.php');
|
||||
config()->set('matomo-proxy-laravel.site_id', '1');
|
||||
|
||||
Http::fake();
|
||||
|
||||
Route::post('/post', fn () => 'ok')
|
||||
->middleware(MatomoProxyLaravelMiddleware::class);
|
||||
|
||||
$this->post('/post')->assertOk();
|
||||
|
||||
Http::assertNothingSent();
|
||||
});
|
||||
|
||||
it('does not send tracking for ignored paths', function () {
|
||||
config()->set('matomo-proxy-laravel.enabled', true);
|
||||
config()->set('matomo-proxy-laravel.base_url', 'https://matomo.example/matomo.php');
|
||||
config()->set('matomo-proxy-laravel.site_id', '1');
|
||||
config()->set('matomo-proxy-laravel.ignore', ['ignored*']);
|
||||
|
||||
Http::fake();
|
||||
|
||||
Route::get('/ignored/page', fn () => 'ok')
|
||||
->middleware(MatomoProxyLaravelMiddleware::class);
|
||||
|
||||
$this->get('/ignored/page')->assertOk();
|
||||
|
||||
Http::assertNothingSent();
|
||||
});
|
||||
|
||||
it('does not send tracking for non-html responses', function () {
|
||||
config()->set('matomo-proxy-laravel.enabled', true);
|
||||
config()->set('matomo-proxy-laravel.base_url', 'https://matomo.example/matomo.php');
|
||||
config()->set('matomo-proxy-laravel.site_id', '1');
|
||||
|
||||
Http::fake();
|
||||
|
||||
Route::get('/json', fn () => response()->json(['ok' => true]))
|
||||
->middleware(MatomoProxyLaravelMiddleware::class);
|
||||
|
||||
$this->get('/json')->assertOk();
|
||||
|
||||
Http::assertNothingSent();
|
||||
});
|
||||
|
||||
it('does not send tracking for 404 responses', function () {
|
||||
config()->set('matomo-proxy-laravel.enabled', true);
|
||||
config()->set('matomo-proxy-laravel.base_url', 'https://matomo.example/matomo.php');
|
||||
config()->set('matomo-proxy-laravel.site_id', '1');
|
||||
|
||||
Http::fake();
|
||||
|
||||
$this->get('/nonexistent')->assertNotFound();
|
||||
|
||||
Http::assertNothingSent();
|
||||
});
|
||||
Reference in new issue
Block a user