takt

Symfony

Le bridge Symfony vskstudio/takt-symfony expose le cœur PHP sous forme de bundle : une fonction Twig pour le snippet et un service Takt autowiré pour le S2S. Compatible Symfony 6.4 / 7.x, PHP 8.1+.

composer require vskstudio/takt-symfony

Le paquet n’expose pas de recette Flex : enregistre le bundle à la main.

// config/bundles.php
return [
    // …
    Vskstudio\Takt\Symfony\TaktBundle::class => ['all' => true],
];

Le TaktBundle enregistre ensuite ses services depuis la config sémantique : le SnippetRenderer derrière la fonction Twig, et le client S2S Takt. Ce dernier est RequestStack-aware : il forwarde l’IP et l’User-Agent de la requête courante pour l’attribution.

Cette page est la référence des options. Pour l’intégration de bout en bout et ses pièges — placement du snippet sous Turbo, arbitrage contrôleur de retour / webhook pour les conversions — voir le guide analytics sans cookie dans une application Symfony.

Configuration

config/packages/takt.yaml :

takt:
    domain: 'exemple.fr'
    endpoint: 'https://taktlytics.com/api/event'   # voir « La clé endpoint » ci-dessous
    api_key: '%env(TAKT_API_KEY)%'                 # S2S uniquement — omets la clé sinon
    mode: inline
    outbound: true
    files: true
    tagged: true
    not_found: true
    file_extensions: ['pdf', 'zip']

Un %env(...)% doit correspondre à une variable déclarée : sans elle, le conteneur lève EnvNotFoundException sur toutes les pages qui rendent le snippet.

# .env
TAKT_API_KEY=tk_…

Référence des clés

CléDéfautRôle
domain''Domaine du site (attribut data-domain, et domaine porté par chaque event S2S)
endpointhttps://taktlytics.comCible d’ingestion — deux contrats distincts, voir ci-dessous
script_originnullOrigine first-party qui sert le runtime et dont l’endpoint est dérivé (contourne les ad-blockers) ; endpoint prime
api_keynullClé d’API scope=ingest — utilisée uniquement par le client S2S
modeinlineSource du runtime : inline, cdn, asset ou sdk
outboundfalseSuit les liens sortants (token outbound)
filesfalseSuit les téléchargements (token downloads)
taggedfalseSuit les éléments marqués data-takt-event (token tagged)
not_foundfalseSuit les pages 404 (token 404)
file_extensions[]Restreint les téléchargements comptés ; vide = liste par défaut du tracker
exclude_localhosttrueIgnore les hits émis depuis localhost — la raison la plus fréquente d’un « rien ne remonte » en dev
noncenullNonce CSP posé sur la balise <script> (indispensable en mode: inline sous CSP stricte)
sample_ratenullN’envoie qu’une fraction (0–1) des hits
track_querynullConserve la query string + le hash (défaut : strippés)
query_params[]Allowlist de paramètres conservés quand track_query est off
exclude[]Préfixes de chemins jamais suivis — requiert mode: sdk
respect_dntnullfalse cesse de respecter Do-Not-Track
enablednullfalse = coupe-circuit, snippet no-op
scrub_urlnullFonction JS brute de réécriture d’URL — requiert mode: sdk

Les cinq dernières options avancées valent null par défaut (« non définie » : le défaut du tracker s’applique) ; ne les pose que pour une valeur non-défaut, sample_rate: 0.5 jette la moitié du trafic et enabled: false coupe toute la mesure.

Les modes :

  • inline — embarque le bundle takt.auto.js vendoré dans la balise (zéro requête en plus).
  • cdn — loader <script defer src="https://cdn.jsdelivr.net/npm/@vskstudio/[email protected]/dist/takt.auto.js">.
  • asset — loader vers /takt/takt.auto.js, une copie que tu héberges toi-même (voir le callout en fin de page).
  • sdk<script type="module"> qui boote le SDK complet.

mode n’est pas validé : une valeur inconnue retombe silencieusement sur inline.

La clé endpoint

La même clé alimente deux consommateurs qui n’attendent pas la même forme :

  • snippet Twig — la valeur est rendue telle quelle en data-endpoint, et le tracker y POSTe : il lui faut une URL d’ingestion complète ;
  • service Takt (S2S) — la valeur est une origine de base, à laquelle /api/event est ajouté à chaque envoi.
Valeur d’endpointLe snippet POSTe surLe service S2S POSTe sur
https://taktlytics.com (défaut)https://taktlytics.comhttps://taktlytics.com/api/event
https://taktlytics.com/api/eventhttps://taktlytics.com/api/eventhttps://taktlytics.com/api/event/api/event
/api/event (proxy same-origin)/api/event sur ton domainechemin relatif : aucun envoi n’aboutit

Aucune valeur ne satisfait les deux usages à la fois. Règle-la selon ce que tu utilises :

  • snippet seulendpoint: 'https://taktlytics.com/api/event' pour la collecte hébergée, ou '/api/event' si tu proxifies l’ingestion en first-party depuis ton propre domaine ;
  • S2S seul — garde le défaut https://taktlytics.com ;
  • les deux dans la même application — règle endpoint pour le snippet et redéclare le client S2S avec l’origine nue :
# config/services.yaml
services:
    Vskstudio\Takt\Takt:
        factory: ['Vskstudio\Takt\Symfony\TaktFactory', 'create']
        arguments:
            - 'https://taktlytics.com'
            - 'exemple.fr'
            - '%env(TAKT_API_KEY)%'
            - '@request_stack'

script_origin ne sert à dériver l’endpoint du snippet que si endpoint vaut exactement https://taktlytics.com/api/event : le tracker POSTe alors sur {script_origin}/api/event. Avec toute autre valeur, l’endpoint est émis en data-endpoint et prime.

mode: sdk

mode: sdk charge le SDK en module ES. C’est le seul mode capable d’exprimer scrub_url, une fonction JS brute de réécriture d’URL injectée verbatim dans la page, et exclude, les préfixes de chemins jamais suivis. Le snippet minimal n’embarque ni l’un ni l’autre : les poser dans un autre mode lève une exception au rendu plutôt que de les laisser filer silencieusement. Réserve scrub_url au dev : ne la construis jamais à partir d’entrées utilisateur.

takt:
    mode: sdk
    exclude: ['/app', '/compte']
    scrub_url: "(u) => u.split('#')[0]"

Fonction Twig takt()

Rends le snippet dans le <head> de ton template de base :

<head>
    <meta charset="utf-8">
    {{ takt() }}
</head>

takt() ne prend aucun argument : l’autocapture (outbound, files, tagged, not_found) et toutes les autres options se règlent uniquement dans config/packages/takt.yaml. Un argument passé depuis un template est ignoré sans erreur — il n’y a pas de surcharge par template.

Service Takt autowiré (serveur-à-serveur)

Type-hinte simplement Vskstudio\Takt\Takt — l’autowiring fait le reste :

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Vskstudio\Takt\Revenue;
use Vskstudio\Takt\Takt;

final class CheckoutController
{
    public function __construct(private readonly Takt $takt) {}

    public function success(): Response
    {
        $this->takt->event('Purchase', ['plan' => 'pro'], new Revenue(amount: '29', currency: 'EUR'));

        return new Response('Merci');
    }
}

L’envoi est fire-and-forget : un 202 vaut succès, et les erreurs de transport sont avalées pour ne jamais casser la requête. $this->takt->strict() renvoie une instance qui lève sur tout statut différent de 202 (utile en test). pageview() et withVisitor() sont détaillés sur la page cœur PHP.

Le service dérive l'IP du visiteur via Request::getClientIp(), qui n'honore X-Forwarded-For que si framework.trusted_proxies est configuré. Derrière un CDN ou un reverse proxy non déclaré, tous les events S2S sont attribués à l'IP du proxy.
En mode: asset, le loader pointe vers /takt/takt.auto.js, mais le bundle ne publie aucun asset : copie toi-même vendor/vskstudio/takt-core-php/resources/takt.auto.js dans public/takt/takt.auto.js. En mode: sdk avec script_origin, le module est chargé depuis /takt/takt.esm.js sur cette origine — ce fichier n'est pas vendoré non plus, c'est à toi de le servir.