Proje: Okul Platform · Hub: Okul Platform — Architecture

GA event flow — end-to-end

Bir FE event’i, call site’dan GA4’e ulaşana kadar 5 katman geçer. Her biri ayrı dosyada yaşıyor; birinin eksik/yanlış yüklenmesi tüm zincirin patlamasına neden olur.

Zincir

┌─────────────────────────────────────────────────────────┐
│ 1. Call site                                             │
│    general.js / app.js / inline blade script            │
│    → gaSendEventOtp('Submit', 'OTPRequest')             │
└───────────────────┬─────────────────────────────────────┘
                    │ (function call)
                    ▼
┌─────────────────────────────────────────────────────────┐
│ 2. Typed helper                                          │
│    resources/assets/scripts/gaEvents.js                 │
│    gaSendEventOtp(...) / gaSendEventLogin(...) / ...    │
│    → ga4EventParseAndSend('Login', 'gaEventLogin', {})  │
└───────────────────┬─────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────┐
│ 3. Generic dispatcher                                    │
│    resources/assets/scripts/gaEvents.js                 │
│    ga4EventParseAndSend(name, customName, params)       │
│    → strips null/empty params                           │
│    → ga4Event(name, customName, params)                 │
└───────────────────┬─────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────┐
│ 4. dataLayer push (Blade-inline)                         │
│    resources/views/includes/events/base.blade.php       │
│    ga4Event(eventName, customEventName, parameters)     │
│    → parameters.event = eventName                       │
│    → parameters.customEventName = customEventName       │
│    → dataLayer.push(parameters)                         │
│    (admin user'larda console.log yapar, push ETMEZ)     │
└───────────────────┬─────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────┐
│ 5. GTM snippet consumer                                  │
│    resources/views/includes/gtm.blade.php               │
│    GTM loader (only if $gtmContainerId is set)          │
│    → GTM yakalar, GA4 tag'ine forward eder              │
│    → GA4 event kaydı                                     │
└─────────────────────────────────────────────────────────┘

Katman detayları + gotcha’lar

Katman 1: Call site

  • Desktop: resources/assets/scripts/frontend/general.js (bundle: main.min.js)
  • Mobile: resources/assets/scripts/mobile/app.js (bundle: mobile.min.js)
  • Page-specific: blade içinde inline <script> (customer login örneği)
  • Kural: helper DEFINE etmez, yalnızca CALL eder. gaSendEventXxx helper’ları gaEvents.js’te olmalı. Bkz. 2026-04-14-ga-event-helper-location.

Katman 2 & 3: gaEvents.js (typed helpers + dispatcher)

  • Dosya: resources/assets/scripts/gaEvents.js
  • webpack.mix.js’te iki yerde:
    1. main.min.js bundle’ına dahil (desktop)
    2. mobile.min.js bundle’ına dahil (mobile)
    3. mix.copy(...) ile public/assets/scripts/gaEvents.js standalone kopya (customer panel gibi ayrı pipeline kullanan sayfalar için, 2026-04-14 eklendi)
  • Tipik helper pattern:
    function gaSendEventYyy(pageType_, action_, ...) {
        ga4EventParseAndSend('Yyy', 'gaEventYyy', { ... });
    }
  • Bağımlılık: ga4Event fonksiyonuna global erişim. Bu fonksiyon gaEvents.js’te YOK → katman 4’te.

Katman 4: includes.events.base (ga4Event + gaEvent)

  • Dosya: resources/views/includes/events/base.blade.php
  • İçerik: inline <script> bloğu — gaEvent (eski Universal Analytics format’ı) ve ga4Event (GA4 custom event format’ı).
  • ga4Event:
    function ga4Event(eventName, customEventName, parameters) {
        parameters.customEventName = customEventName;
        parameters.event = eventName;
        dataLayer.push(parameters);  // ← dataLayer undefined ise ReferenceError!
    }
  • Admin user özel davranış: @if(Auth::check() && Auth::user()->isAdmin())console.log yapar, dataLayer.push ETMEZ. Admin test session’ında event’ler GA’ya gitmez, sadece console’a düşer.
  • Gotcha 1 (incident 2026-04-14): gaEvents.js’in çalışması için bu blade mutlaka önce include edilmiş olmalı. Public site layout’ları onu otomatik include ediyor; customer login gibi ayrı pipeline’larda manuel @include('includes.events.base') gerekli. Bkz. 2026-04-14-gaevents-missing-base.

Katman 5: GTM loader + iframe

  • Dosya: resources/views/includes/gtm.blade.php (head script) + gtm-iframe.blade.php (body noscript fallback)
  • Container ID env’den okunur:
    • Public school site (B2C): GTM_B2C_CONTAINER_IDconfig('services.gtm.b2c_container_id')
    • Customer/kurumsal (B2B): GTM_B2B_CONTAINER_IDconfig('services.gtm.b2b_container_id')
  • @empty(!$gtmContainerId) guard: env var boşsa GTM snippet HİÇ render edilmez, dataLayer initialize edilmez.
  • Gotcha 2 (incident 2026-04-14): local/dev ortamlarda env var yoksa ga4Event’in dataLayer.push çağrısı ReferenceError: dataLayer is not defined fırlatır. Çözüm: <script>window.dataLayer = window.dataLayer || [];</script> shim’i gtm include’dan ÖNCE veya en azından events.base’den önce eklemek — env boş olsa bile boş array yaratır, push sessizce no-op olur.

Doğru yükleme sırası (page head)

{{-- 1. dataLayer shim (GTM config boş olsa bile güvenli) --}}
<script>window.dataLayer = window.dataLayer || [];</script>
 
{{-- 2. GTM snippet (config doluysa render eder) --}}
@include('includes.gtm', ['gtmContainerId' => config('services.gtm.xxx_container_id')])
 
{{-- 3. ga4Event / gaEvent inline tanımı --}}
@include('includes.events.base')
 
{{-- 4. Tipli helper'lar --}}
<script src="{{ asset('assets/scripts/gaEvents.js') }}"></script>
 
{{-- 5. pageType globali (isteğe bağlı ama event'lerde default olarak kullanılıyor) --}}
<script>var _gaEventCat = 'PageName';</script>
 
{{-- Body'de GTM noscript iframe --}}
@include('includes.gtm-iframe', ['gtmContainerId' => config('services.gtm.xxx_container_id')])

Bundle edilmiş sayfalar (public/mobile) için 3, 4 zaten main.min.js/mobile.min.js içinde — sadece 1, 2 ve 5 layout tarafından sağlanır.

Hata fırlatma özeti

HataOlası neden
ReferenceError: ga4Event is not definedincludes.events.base include EDİLMEMİŞ.
ReferenceError: dataLayer is not definedGTM snippet render edilmemiş (env var boş) VE shim eklenmemiş.
ReferenceError: gaSendEventLogin is not definedgaEvents.js yüklenmemiş (bundle dahil değil, standalone <script src> eksik).
ReferenceError: _csrfToken / _gaEventCat is not definedLayout’ın <script>var _xxx = '...'</script> init’i eksik.

Debugging

  1. dataLayer array’ini console’da incele: dataLayer.push’lar görünüyor mu?
  2. Admin user değilsen push’lar GA’ya gitmeli. Admin isen sadece console.log.
  3. GTM Preview mode açıp browser’da gtm.js yüklenip yüklenmediğini ve event’leri dinle.
  4. GA4 DebugView’da real-time event kaydını izle.