Skip to content

πŸ“… Google Calendar Integration - Soluzione Robusta ​

Documentazione completa dell'integrazione Google Calendar bidirezionale per Blade CRM.


🎯 FUNZIONALITΓ€ ​

1. SINCRONIZZAZIONE BIDIREZIONALE ​

Google β†’ Blade (Blocca Slot Occupati) ​

  • Legge eventi da Google Calendar
  • Filtra slot occupati in booking page
  • Aggiornamento ogni 5 minuti (cache)
  • Filtri avanzati (eventi cancelled, free, declined)

Blade β†’ Google (Crea Eventi) ​

  • Crea evento quando ospite prenota
  • Titolo personalizzato
  • Google Meet link automatico
  • Invita ospite via email
  • Descrizione con dettagli appuntamento

πŸ—οΈ ARCHITETTURA ​

API Google Usata: Events.list + Events.insert ​

NON usiamo:

  • ❌ FreeBusy API (troppo limitata)
  • ❌ Sync Tokens (troppo complesso)
  • ❌ Package Spatie (vogliamo controllo totale)

Usiamo:

  • βœ… Google API Client ufficiale (google/apiclient)
  • βœ… Events.list per lettura
  • βœ… Events.insert per scrittura
  • βœ… Cache 5 minuti per performance

πŸ“– CODICE CORE ​

1. Lettura Eventi (Google β†’ Blade) ​

php
// app/Services/GoogleCalendar/GoogleCalendarService.php

public function getEventsForDay(UserCalendar $calendar, Carbon $startOfDay, Carbon $endOfDay): array
{
    $client = $this->getClientForCalendar($calendar);
    $service = new GoogleCalendar($client);
    
    // Lista eventi per il giorno
    $events = $service->events->listEvents($calendar->google_calendar_id, [
        'timeMin' => $startOfDay->toRfc3339String(),
        'timeMax' => $endOfDay->toRfc3339String(),
        'singleEvents' => true,   // Espande ricorrenti
        'orderBy' => 'startTime',
        'maxResults' => 100,
    ]);
    
    // Estrai busy times con filtri avanzati
    return $this->extractBusyTimesFromEvents($events->getItems(), $startOfDay, $endOfDay);
}

Filtri applicati:

  • βœ… Eventi cancelled β†’ Ignorati
  • βœ… Eventi transparency: transparent β†’ Ignorati (non bloccano calendario)
  • βœ… Eventi declined β†’ Ignorati
  • βœ… Eventi all-day β†’ Corretti (no +1 giorno)
  • βœ… Timezone β†’ Rispettato

2. Scrittura Eventi (Blade β†’ Google) ​

php
// app/Jobs/CreateGoogleCalendarEventJob.php

$eventData = [
    'summary' => 'Meeting con Mario Rossi',  // Titolo personalizzato
    'description' => 'Note: ...\nTelefono: ...',  // Descrizione completa
    'start' => [
        'dateTime' => '2025-11-04T10:00:00+01:00',
        'timeZone' => 'Europe/Rome',
    ],
    'end' => [
        'dateTime' => '2025-11-04T10:30:00+01:00',
        'timeZone' => 'Europe/Rome',
    ],
    'attendees' => [
        ['email' => 'ospite@example.com'],  // Invita ospite
    ],
    'conferenceData' => [  // Google Meet
        'createRequest' => [
            'requestId' => 'appointment-123',
            'conferenceSolutionKey' => ['type' => 'hangoutsMeet'],
        ],
    ],
];

$event = $googleCalendar->createEventOnCalendar($calendar, $eventData);
$meetLink = $event->getHangoutLink();  // Link Google Meet

Feature supportate:

  • βœ… Titolo personalizzato con nome ospite e company
  • βœ… Google Meet link automatico
  • βœ… Inviti via email agli attendees
  • βœ… Descrizione con note ospite e telefono
  • βœ… Location (in-person, phone, video)
  • βœ… Reminders personalizzati

βš™οΈ CONFIGURAZIONE ​

1. OAuth Google Calendar ​

File: config/services.php

php
'google_calendar' => [
    'client_id' => env('GOOGLE_CALENDAR_CLIENT_ID'),
    'client_secret' => env('GOOGLE_CALENDAR_CLIENT_SECRET'),
    'redirect_uri' => env('GOOGLE_CALENDAR_REDIRECT_URI'),
    'scopes' => [
        'https://www.googleapis.com/auth/calendar',
        'https://www.googleapis.com/auth/calendar.events',
    ],
    'calendar_id' => env('GOOGLE_CALENDAR_ID', 'primary'),
],

2. Variabili .env ​

env
GOOGLE_CALENDAR_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_CALENDAR_CLIENT_SECRET=xxx
GOOGLE_CALENDAR_REDIRECT_URI=https://tuodominio.com/oauth/google-calendar/callback
GOOGLE_CALENDAR_ID=primary

πŸ”„ FLUSSO COMPLETO ​

1. Connessione Google Calendar ​

User β†’ Dashboard β†’ Google Calendar β†’ Connetti
  ↓
OAuth redirect a Google
  ↓
User autorizza
  ↓
Callback salva tokens (encrypted)
  ↓
UserCalendar creato con:
  - access_token (encrypted)
  - refresh_token (encrypted)
  - expires_at

2. Prenotazione Ospite ​

Ospite β†’ Booking Page β†’ Seleziona slot
  ↓
Frontend controlla isDayAvailable (weekly_hours)
  ↓
Backend:
  1. Genera slot teorici (weekly_hours)
  2. Filtra appuntamenti locali
  3. Chiama getEventsForDay() β†’ busy times Google
  4. Filtra slot occupati
  5. Ritorna slot disponibili
  ↓
Ospite seleziona slot e compila form
  ↓
Backend:
  1. Verifica slot disponibile
  2. Crea Appointment
  3. Dispatch CreateGoogleCalendarEventJob
  ↓
Job:
  1. Crea evento su Google Calendar
  2. Ottiene Google Meet link
  3. Aggiorna Appointment con google_event_id e meet_link
  ↓
Ospite riceve email con Meet link

3. Lettura Google Calendar (ogni richiesta slot) ​

Frontend richiede slot β†’ /booking/qw/slots?date=2025-11-04
  ↓
Backend BookingController
  ↓
BookingAvailabilityService:
  1. Genera slot teorici
  2. Filtra locali
  3. Per ogni calendario associato:
     - Controlla cache (5 min TTL)
     - Se cache miss: getEventsForDay()
     - Estrae busy times
     - Filtra slot
  ↓
Ritorna slot disponibili (senza occupati Google)

πŸ§ͺ TESTING ​

Test 1: Lettura Google Calendar ​

bash
# 1. Crea evento in Google Calendar
#    Titolo: "Test Busy"
#    Data: Domani 10:00-11:00

# 2. Aspetta 5 min (o clear cache)
php artisan cache:clear

# 3. Test API
php artisan google:test-busy qw {domani}

# Output atteso:
# βœ“ API call successful
# Busy periods found: 1
#   β€’ 10:00 - 11:00 (Test Busy)

Test 2: Scrittura su Google ​

bash
# 1. Prenota da booking page
#    https://blade.laravel.cloud/booking/qw
#    Slot: Domani 14:00

# 2. Verifica job processato
php artisan booking:check-last

# Output atteso:
# βœ“ Google Event ID: abc123
# βœ“ Meet Link: https://meet.google.com/xxx-yyy-zzz

# 3. Verifica Google Calendar
#    https://calendar.google.com
#    Evento "Meeting con..." presente βœ…

Test 3: Filtri Avanzati ​

bash
# 1. Crea evento "Free" in Google (Show as: Free)
# 2. Booking page β†’ slot DISPONIBILE βœ… (non bloccato)

# 3. Crea evento normale in Google  
# 4. Booking page β†’ slot OCCUPATO ❌ (bloccato)

πŸ“Š PERFORMANCE ​

OperazioneTempoCacheNote
Lettura eventi~100-200ms5 minCon GZIP compression
Cache hit~5ms-Nessuna API call
Creazione evento~200-400ms-Include Meet link
Cambio mese~100-300ms-Loading indicator

πŸ”§ COMANDI UTILI ​

bash
# Test busy times
php artisan google:test-busy qw 2025-11-04

# Analisi timezone
php artisan google:analyze-timezone qw 2025-11-04

# Diagnosi backend
php artisan booking:diagnose-backend qw 2025-11-04

# Check ultimo appointment
php artisan booking:check-last

# Clear cache Google
php artisan cache:clear

βœ… CHECKLIST FUNZIONAMENTO ​

  • [ ] Google Calendar connesso (/user-calendars)
  • [ ] Token presente (access + refresh)
  • [ ] Calendario associato a booking page
  • [ ] Weekly hours configurati (Lun-Ven 09:00-17:00)
  • [ ] Test google:test-busy β†’ mostra eventi Google
  • [ ] Booking page mostra slot disponibili
  • [ ] Slot occupati Google NON visibili in booking page
  • [ ] Prenotazione crea evento in Google Calendar
  • [ ] Google Meet link generato
  • [ ] Ospite riceve invito email

πŸŽ‰ CONCLUSIONE ​

Sistema finale:

  • βœ… Semplice (50 righe vs 250)
  • βœ… Robusto (Google API ufficiale)
  • βœ… Completo (tutte feature richieste)
  • βœ… Performante (GZIP + cache 5 min)
  • βœ… Manutenibile (codice pulito, logging dettagliato)

Zero dipendenze esterne (solo google/apiclient giΓ  installato).

Pronto per produzione! πŸš€

Realizzato con ❀️ da DScom