├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── analytics-tracker.php ├── psalm.xml.dist └── src ├── AnalyticsBag.php ├── AnalyticsTracker.php ├── AnalyticsTrackerServiceProvider.php ├── Helpers ├── Request.php └── Url.php ├── Middleware └── TrackAnalyticsParametersMiddleware.php └── Sources ├── CrossOriginRequestHeader.php ├── CrossOriginRequestParameter.php ├── RequestHeader.php ├── RequestParameter.php └── Source.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-utm-forwarder` will be documented in this file 4 | 5 | ## 1.0.0 - 202X-XX-XX 6 | 7 | - initial release 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Spatie bvba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## UNFINISHED & NOT ON PACKAGIST! 2 | 3 | # Keeps track of the original UTM (or other analytics) parameters 4 | 5 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/laravel-analytics-tracker.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-analytics-tracker) 6 | [![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/spatie/laravel-analytics-tracker/run-tests?label=tests)](https://github.com/spatie/laravel-analytics-tracker/actions?query=workflow%3Arun-tests+branch%3Amaster) 7 | [![Total Downloads](https://img.shields.io/packagist/dt/spatie/laravel-analytics-tracker.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-analytics-tracker) 8 | 9 | Cross domain analytics is hard. This package helps you to keep track of the visitor's original UTM parameters, referer header and other analytics parameters. You can then submit these parameters along with a form submission or add them to a link to another domain you track. 10 | 11 | ## Support us 12 | 13 | [](https://spatie.be/github-ad-click/laravel-utm-forwarder) 14 | 15 | 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). 16 | 17 | 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). 18 | 19 | ## Installation 20 | 21 | You can install the package via composer: 22 | 23 | ```bash 24 | composer require spatie/laravel-analytics-tracker 25 | ``` 26 | 27 | The package works via a middleware that needs to be added to the `web` stack in your `kernel.php` file. Make sure to register this middleware after the `StartSession` middleware. 28 | 29 | ```php 30 | // app/Http/Kernel.php 31 | 32 | protected $middlewareGroups = [ 33 | 'web' => [ 34 | // ... 35 | \Illuminate\Session\Middleware\StartSession::class, 36 | // ... 37 | 38 | \Spatie\AnalyticsTracker\Middleware\TrackAnalyticsParametersMiddleware::class, 39 | ], 40 | ]; 41 | ``` 42 | 43 | To configure the tracked parameters or how they're mapped on the URL parameters, you can publish the config file using: 44 | 45 | ```bash 46 | php artisan vendor:publish --provider="Spatie\AnalyticsTracker\AnalyticsTrackerServiceProvider" 47 | ``` 48 | 49 | This is the contents of the published config file: 50 | 51 | ```php 52 | return [ 53 | /* 54 | * These are the analytics parameters that will be tracked when a user first visits 55 | * the application. The configuration consists of the parameter's key and the 56 | * source to extract this key from. 57 | * 58 | * Available sources can be found in the `\Spatie\AnalyticsTracker\Sources` namespace. 59 | */ 60 | 'tracked_parameters' => [ 61 | [ 62 | 'key' => 'utm_source', 63 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 64 | ], 65 | [ 66 | 'key' => 'utm_medium', 67 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 68 | ], 69 | [ 70 | 'key' => 'utm_campaign', 71 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 72 | ], 73 | [ 74 | 'key' => 'utm_term', 75 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 76 | ], 77 | [ 78 | 'key' => 'utm_content', 79 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 80 | ], 81 | [ 82 | 'key' => 'referer', 83 | 'source' => Spatie\AnalyticsTracker\Sources\CrossOriginRequestHeader::class, 84 | ], 85 | ], 86 | 87 | /** 88 | * We'll put the tracked parameters in the session using this key. 89 | */ 90 | 'session_key' => 'tracked_analytics_parameters', 91 | 92 | /* 93 | * When formatting an URL to add the tracked parameters we'll use the following 94 | * mapping to put tracked parameters in URL parameters. 95 | * 96 | * This is useful when using an analytics solution that ignores the utm_* parameters. 97 | */ 98 | 'parameter_url_mapping' => [ 99 | 'utm_source' => 'utm_source', 100 | 'utm_medium' => 'utm_medium', 101 | 'utm_campaign' => 'utm_campaign', 102 | 'utm_term' => 'utm_term', 103 | 'utm_content' => 'utm_content', 104 | 'referer' => 'referer', 105 | ], 106 | ]; 107 | ``` 108 | 109 | ## Usage 110 | 111 | The easiest way to retrieve the tracked parameters is by resolving the `TrackedAnalyticsParameters` class: 112 | 113 | ```php 114 | use Spatie\AnalyticsTracker\AnalyticsBag; 115 | 116 | app(AnalyticsBag::class)->get(); // returns an array of tracked parameters 117 | ``` 118 | 119 | You can also decorate an existing URL with the tracked parameters. This is useful to forward analytics to another domain you're running analytics on. 120 | 121 | ```blade 122 | 123 | Buy this product on our webshop 124 | 125 | 126 | Will link to https://mywebshop.com?utm_source=facebook&utm_campaign=blogpost 127 | ``` 128 | 129 | ## Testing 130 | 131 | ``` bash 132 | composer test 133 | ``` 134 | 135 | ## Changelog 136 | 137 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 138 | 139 | ## Contributing 140 | 141 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 142 | 143 | ## Security Vulnerabilities 144 | 145 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 146 | 147 | ## Credits 148 | 149 | - [Alex Vanderbist](https://github.com/AlexVanderbist) 150 | - [All Contributors](../../contributors) 151 | 152 | ## License 153 | 154 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 155 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spatie/laravel-analytics-tracker", 3 | "description": "Keeps track of the original UTM parameters", 4 | "keywords": [ 5 | "spatie", 6 | "laravel-utm-forwarder" 7 | ], 8 | "homepage": "https://github.com/spatie/laravel-utm-forwarder", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Alex Vanderbist", 13 | "email": "alex.vanderbist@gmail.com", 14 | "homepage": "https://spatie.be", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^7.4", 20 | "illuminate/contracts": "^8.0" 21 | }, 22 | "require-dev": { 23 | "friendsofphp/php-cs-fixer": "^2.16", 24 | "orchestra/testbench": "^6.0", 25 | "phpunit/phpunit": "^9.3", 26 | "vimeo/psalm": "^3.11" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "Spatie\\AnalyticsTracker\\": "src" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Spatie\\AnalyticsTracker\\Tests\\": "tests" 36 | } 37 | }, 38 | "scripts": { 39 | "psalm": "vendor/bin/psalm", 40 | "test": "vendor/bin/phpunit --colors=always", 41 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage", 42 | "format": "vendor/bin/php-cs-fixer fix --allow-risky=yes" 43 | }, 44 | "config": { 45 | "sort-packages": true 46 | }, 47 | "extra": { 48 | "laravel": { 49 | "providers": [ 50 | "AnalyticsTrackerServiceProvider" 51 | ] 52 | } 53 | }, 54 | "minimum-stability": "dev", 55 | "prefer-stable": true 56 | } 57 | -------------------------------------------------------------------------------- /config/analytics-tracker.php: -------------------------------------------------------------------------------- 1 | [ 12 | [ 13 | 'key' => 'utm_source', 14 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 15 | ], 16 | [ 17 | 'key' => 'utm_medium', 18 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 19 | ], 20 | [ 21 | 'key' => 'utm_campaign', 22 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 23 | ], 24 | [ 25 | 'key' => 'utm_term', 26 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 27 | ], 28 | [ 29 | 'key' => 'utm_content', 30 | 'source' => Spatie\AnalyticsTracker\Sources\RequestParameter::class, 31 | ], 32 | [ 33 | 'key' => 'referer', 34 | 'source' => Spatie\AnalyticsTracker\Sources\CrossOriginRequestHeader::class, 35 | ], 36 | ], 37 | 38 | /** 39 | * We'll put the tracked parameters in the session using this key. 40 | */ 41 | 'session_key' => 'tracked_analytics_parameters', 42 | 43 | /* 44 | * When formatting an URL to add the tracked parameters we'll use the following 45 | * mapping to put tracked parameters in URL parameters. 46 | * 47 | * This is useful when using an analytics solution that ignores the utm_* parameters. 48 | */ 49 | 'parameter_url_mapping' => [ 50 | 'utm_source' => 'utm_source', 51 | 'utm_medium' => 'utm_medium', 52 | 'utm_campaign' => 'utm_campaign', 53 | 'utm_term' => 'utm_term', 54 | 'utm_content' => 'utm_content', 55 | 'referer' => 'referer', 56 | ], 57 | ]; 58 | -------------------------------------------------------------------------------- /psalm.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/AnalyticsBag.php: -------------------------------------------------------------------------------- 1 | session = $session; 20 | $this->trackedParameters = $trackedParameters; 21 | $this->sessionKey = $sessionKey; 22 | } 23 | 24 | public function putFromRequest(Request $request) 25 | { 26 | $parameters = $this->determineFromRequest($request); 27 | 28 | $this->session->put($this->sessionKey, $parameters); 29 | } 30 | 31 | public function get(): array 32 | { 33 | return $this->session->get($this->sessionKey, []); 34 | } 35 | 36 | protected function determineFromRequest(Request $request): array 37 | { 38 | return collect($this->trackedParameters) 39 | ->mapWithKeys(function ($trackedParameter) use ($request) { 40 | $source = new $trackedParameter['source']($request); 41 | 42 | return [$trackedParameter['key'] => $source->get($trackedParameter['key'])]; 43 | }) 44 | ->filter() 45 | ->toArray(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AnalyticsTracker.php: -------------------------------------------------------------------------------- 1 | analyticsBag = $analyticsBag; 14 | } 15 | 16 | public function get(): array 17 | { 18 | return $this->analyticsBag->get(); 19 | } 20 | 21 | public function decorateUrl(string $url): string 22 | { 23 | $analyticsParameters = $this->analyticsBag->get(); 24 | $analyticsParameters = $this->mapParametersToUrlParameters($analyticsParameters); 25 | 26 | return Url::addParameters($url, $analyticsParameters); 27 | } 28 | 29 | protected function mapParametersToUrlParameters(array $parameters): array 30 | { 31 | $mapping = config('analytics-tracker.parameter_url_mapping'); 32 | 33 | return collect($parameters) 34 | ->mapWithKeys(fn (string $value, string $parameter) => [$mapping[$parameter] ?? $parameter => $value]) 35 | ->toArray(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/AnalyticsTrackerServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 13 | $this->publishes([ 14 | __DIR__ . '/../config/analytics-tracker.php' => config_path('analytics-tracker.php'), 15 | ], 'config'); 16 | } 17 | } 18 | 19 | public function register() 20 | { 21 | $this->mergeConfigFrom(__DIR__ . '/../config/analytics-tracker.php', 'analytics-tracker'); 22 | 23 | $this->app->singleton(AnalyticsBag::class, function ($app) { 24 | return new AnalyticsBag( 25 | $app->make(Session::class), 26 | config('analytics-tracker.tracked_parameters'), 27 | config('analytics-tracker.session_key'), 28 | ); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Helpers/Request.php: -------------------------------------------------------------------------------- 1 | header('referer') ?? ''); 10 | 11 | return $refererHost !== $request->getHost(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Helpers/Url.php: -------------------------------------------------------------------------------- 1 | analyticsBag = $analyticsBag; 16 | } 17 | 18 | public function handle(Request $request, Closure $next) 19 | { 20 | $this->analyticsBag->putFromRequest($request); 21 | 22 | return $next($request); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Sources/CrossOriginRequestHeader.php: -------------------------------------------------------------------------------- 1 | request)) { 12 | return null; 13 | } 14 | 15 | return parent::get($key); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Sources/CrossOriginRequestParameter.php: -------------------------------------------------------------------------------- 1 | request)) { 12 | return null; 13 | } 14 | 15 | return parent::get($key); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Sources/RequestHeader.php: -------------------------------------------------------------------------------- 1 | request = $request; 14 | } 15 | 16 | public function get(string $key): ?string 17 | { 18 | return $this->request->header($key); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Sources/RequestParameter.php: -------------------------------------------------------------------------------- 1 | request = $request; 14 | } 15 | 16 | public function get(string $key): ?string 17 | { 18 | return $this->request->get($key); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Sources/Source.php: -------------------------------------------------------------------------------- 1 |