PHPackages                             clicktrail/symfony-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. clicktrail/symfony-bundle

ActiveSymfony-bundle[Templating &amp; Views](/categories/templating)

clicktrail/symfony-bundle
=========================

Symfony bundle for ClickTrail attribution: config tree, request capture subscriber, consent resolver gate, Messenger delivery, Twig helpers, diagnostics command, webhook signature verification.

v0.1.1(today)00MITPHPPHP &gt;=8.1CI passing

Since Aug 25Pushed todayCompare

[ Source](https://github.com/vizuh/clicktrail-symfony)[ Packagist](https://packagist.org/packages/clicktrail/symfony-bundle)[ RSS](/packages/clicktrail-symfony-bundle/feed)WikiDiscussions main Synced today

READMEChangelog (2)Dependencies (7)Versions (3)Used By (0)

[English](README.md) | [Português](README.pt-BR.md) | [Deutsch](README.de.md) | [中文](README.zh-CN.md)

**clicktrail/symfony-bundle**

Request capture, consent gating, Messenger delivery and Twig helpers for deterministic campaign attribution — in any Symfony 6.4 / 7.x app.

[![CI](https://github.com/vizuh/clicktrail-symfony/actions/workflows/ci.yml/badge.svg)](https://github.com/vizuh/clicktrail-symfony/actions/workflows/ci.yml)[![Latest Version on Packagist](https://camo.githubusercontent.com/77a3ed8c0f131d7d4f214a34e8745c36edfd654c85d55cb9a88a5683cc07c8df/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f636c69636b747261696c2f73796d666f6e792d62756e646c652e737667)](https://packagist.org/packages/clicktrail/symfony-bundle)[![License: MIT](https://camo.githubusercontent.com/08cef40a9105b6526ca22088bc514fbfdbc9aac1ddbf8d4e6c750e3a88a44dca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667)](LICENSE)

Index
-----

[](#index)

- [Why](#why)
- [Installation](#installation)
- [Quick start](#quick-start)
- [Reading attribution](#reading-attribution)
- [Twig helpers](#twig-helpers)
- [Async delivery via Messenger](#async-delivery-via-messenger)
- [Consent](#consent)
- [Diagnostics](#diagnostics)
- [Webhook signatures](#webhook-signatures)
- [Flex recipe plan](#flex-recipe-plan)
- [Not included (deliberate)](#not-included-deliberate)
- [How it differs](#how-it-differs)
- [Testing](#testing)
- [License](#license)

Why
---

[](#why)

Most tracking packages store what a page showed. ClickTrail proves which campaign created the lead or sale. This bundle is a thin adapter over [`clicktrail/php-sdk`](https://github.com/vizuh/clicktrail-php), which owns the deterministic parse/classify/merge core; the bundle owns Symfony effects: request subscriber, consent gate, Messenger delivery, Twig helpers, diagnostics.

Installation
------------

[](#installation)

```
composer require clicktrail/symfony-bundle
```

Requires PHP **&gt;= 8.1**. (The `clicktrail/php-sdk` repository must be resolvable; a path repo and a VCS fallback are declared in this package's `composer.json`.)

Quick start
-----------

[](#quick-start)

Create `config/packages/clicktrail.yaml`:

```
clicktrail:
    site_id: '%env(string:CLICKTRAIL_SITE_ID)%'
    api_key: '%env(string:CLICKTRAIL_API_KEY)%'
    endpoint: '%env(CLICKTRAIL_ENDPOINT)%'
    consent_required: true        # unknown consent = denied (default true)
    delivery:
        transport: sync           # sync|async (async routes via Messenger)
    resolver_class: null          # FQCN implementing ConsentResolverInterface
```

All values pass through Symfony env processors (`%env(...)%` placeholders). The bundle auto-registers itself; from here every request with campaign parameters builds attribution state:

```
// 1. A visitor arrives from Google Ads on any route.
//    RequestSubscriber merges the touch on kernel.request (high priority):

// 2. In a controller or service:
use ClickTrail\Symfony\Attribution\ContextHolder;

public function form(ContextHolder $holder): Response
{
    $context = $holder->get();       // AttributionContext for this request (or null)
    // $context?->attribution->first->source === 'google',
    // $context?->attribution->first->clickIds['gclid'] set — persisted to the
    // session ONLY when consent permits analytics storage; unknown = denied.
}

// 3. On conversion, dispatch delivery:
$this->bus->dispatch(new \ClickTrail\Symfony\Messenger\DeliverEventsMessage());
// handler flushes the SDK BatchClient — batched POST to endpoint with
// idempotency keys; nothing is sent during the request itself.
```

A direct visit afterwards changes nothing — first touch stays, stored last touch persists. That is the SDK's merge law, tested, not promised.

Reading attribution
-------------------

[](#reading-attribution)

`Attribution\ContextHolder` is a stateless read-side accessor for the current request's `AttributionContext`. The subscriber stores it as a request attribute, so controllers, services, and event listeners read the same merged state.

Twig helpers
------------

[](#twig-helpers)

```
{# renders the first-party loader  tag from config #}
{{ clicktrail_head(context) }}

{# hidden attribution inputs inside a , so the server-side submit
   carries source / click IDs verbatim #}
{{ clicktrail_hidden_attribution_inputs(attribution) }}
```

Render-only extensions; all output is escaped with `htmlspecialchars(..., ENT_QUOTES)`.

Async delivery via Messenger
----------------------------

[](#async-delivery-via-messenger)

Route the delivery message to your transport:

```
framework:
    messenger:
        routing:
            ClickTrail\Symfony\Messenger\DeliverEventsMessage: async
```

The handler flushes the SDK `BatchClient`. The container must provide PSR-18 client/request/stream factories (e.g. `symfony/http-client`). Delivery never happens during the request unless configured.

Consent
-------

[](#consent)

ClickTrail is a consent consumer, not a CMP. Set `resolver_class` to an FQCN implementing `ConsentResolverInterface`, or override the alias with your own CMP adapter. Until then the shipped `NullConsentResolver` returns an unknown snapshot, treated as denied everywhere: no identifiers are persisted and no events are delivered.

Diagnostics
-----------

[](#diagnostics)

```
php bin/console clicktrail:diagnose
```

Prints the effective configuration (secrets masked) and runs a local signature self-test.

Webhook signatures
------------------

[](#webhook-signatures)

Verify ClickTrail webhook callbacks with constant-time SHA-256 comparison:

```
\ClickTrail\Symfony\Support\WebhookSignature::verify($payload, $signatureHeader, $secret);
// === true only when the signature matches; constant-time, no timing leak
```

Flex recipe plan
----------------

[](#flex-recipe-plan)

A `symfony/recipes-contrib` pull request providing the default `config/packages/clicktrail.yaml` skeleton is planned **post-release** — the recipe cannot be submitted before the package has a tagged version. Until then, create the config file manually as shown above.

Not included (deliberate)
-------------------------

[](#not-included-deliberate)

**Doctrine integration** (persisting attribution snapshots to entities, doctrine event listeners) is intentionally out of scope here. It is planned as an optional follow-up package so apps that do not use ORM keep a dependency-free install.

How it differs
--------------

[](#how-it-differs)

- **DIY UTM-to-cookie snippets** store whatever the URL carried, unvalidated. ClickTrail applies deterministic first/last-touch merge laws validated by golden fixtures shared with our WordPress and GTM engines, gates persistence on consent, and delivers batched events with idempotency keys.
- **DirectoryTree/Metrics** counts anonymous events. Complementary — ClickTrail connects campaigns to identities and revenue, not page-view counters.

See `../docs/COMPETITOR-NOTES.md` for the full analysis.

Testing
-------

[](#testing)

```
php tests/_runner.php                 # full suite, standalone (no kernel boot)
```

CI lints all PHP files on PHP 8.1–8.3 (`.github/workflows/ci.yml`, canonical template from `../templates/ci-php-matrix.yml`).

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity33

Early-stage or recently created project

 Bus Factor1

Top contributor holds 88.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/20295730?v=4)[Hugo ](/maintainers/Atroci)[@Atroci](https://github.com/Atroci)

---

Top Contributors

[![Atroci](https://avatars.githubusercontent.com/u/20295730?v=4)](https://github.com/Atroci "Atroci (8 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

analyticsattributionclickstreamconsent-managementconversion-trackingfirst-party-datagdprmarketing-analyticsmessengerphpsymfonysymfony-bundletwigutmsymfonybundleMessengerUTMattributionconsentgclid

### Embed Badge

![Health badge](/badges/clicktrail-symfony-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/clicktrail-symfony-bundle/health.svg)](https://phpackages.com/packages/clicktrail-symfony-bundle)
```

###  Alternatives

[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

595.8M684](/packages/shopware-core)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.5k6.0M780](/packages/sylius-sylius)[shopware/platform

The Shopware e-commerce core

3.4k1.5M3](/packages/shopware-platform)[contao/core-bundle

Contao Open Source CMS

1301.7M3.1k](/packages/contao-core-bundle)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M236](/packages/sulu-sulu)[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k18.3M433](/packages/easycorp-easyadmin-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
