Files
ladill-events/app/Services/Meet/EventMeetRoomLinkService.php
T
isaaccladandCursor 32c721b4f2
Deploy Ladill Events / deploy (push) Successful in 1m17s
Add service API for Meet to list and link virtual events.
Meet can attach existing conference or webinar rooms to event virtual sessions so registration stays in Events.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 07:38:56 +00:00

149 lines
5.9 KiB
PHP

<?php
namespace App\Services\Meet;
use App\Models\QrCode;
use App\Models\User;
use Illuminate\Support\Str;
class EventMeetRoomLinkService
{
public function __construct(private readonly EventMeetSyncService $sync) {}
/**
* @return list<array<string, mixed>>
*/
public function linkableEvents(User $owner): array
{
return QrCode::query()
->where('user_id', $owner->id)
->where('type', QrCode::TYPE_EVENT)
->orderByDesc('updated_at')
->get()
->filter(function (QrCode $event) {
$format = (string) ($event->content()['format'] ?? 'in_person');
return in_array($format, ['virtual', 'hybrid'], true);
})
->map(fn (QrCode $event) => $this->summarizeEvent($event))
->values()
->all();
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>
*/
public function linkRoom(User $owner, QrCode $event, array $payload): array
{
abort_unless($event->user_id === $owner->id, 403);
abort_unless($event->type === QrCode::TYPE_EVENT, 422);
$format = (string) ($event->content()['format'] ?? 'in_person');
abort_unless(in_array($format, ['virtual', 'hybrid'], true), 422, 'Only virtual or hybrid events can be linked.');
$roomUuid = trim((string) ($payload['meet_room_uuid'] ?? ''));
$roomType = in_array($payload['meet_room_type'] ?? '', ['town_hall', 'webinar'], true)
? (string) $payload['meet_room_type']
: 'town_hall';
abort_unless($roomUuid !== '', 422, 'meet_room_uuid is required.');
$joinUrl = trim((string) ($payload['join_url'] ?? ''));
$title = trim((string) ($payload['title'] ?? $event->label ?? 'Virtual session'));
$sessionId = trim((string) ($payload['virtual_session_id'] ?? ''));
$content = $event->content();
$sessions = array_values(array_filter(
(array) ($content['virtual_sessions'] ?? []),
fn ($session) => is_array($session),
));
$matchedIndex = null;
if ($sessionId !== '') {
foreach ($sessions as $index => $session) {
if ((string) ($session['id'] ?? '') === $sessionId) {
$matchedIndex = $index;
break;
}
}
}
if ($matchedIndex === null) {
foreach ($sessions as $index => $session) {
$existingUuid = trim((string) ($session['meet_room_uuid'] ?? ''));
$existingType = (string) ($session['meet_room_type'] ?? 'town_hall');
if ($existingUuid === '' && $existingType === $roomType) {
$matchedIndex = $index;
break;
}
}
}
if ($matchedIndex === null) {
$sessionId = $sessionId !== '' ? $sessionId : 'vs-'.Str::lower(Str::random(12));
$sessions[] = [
'id' => $sessionId,
'title' => $title,
'meet_room_type' => $roomType,
'scheduled_at' => $payload['scheduled_at'] ?? $content['starts_at'] ?? null,
'duration_minutes' => max(15, (int) ($payload['duration_minutes'] ?? 60)),
];
$matchedIndex = count($sessions) - 1;
} else {
$sessionId = (string) ($sessions[$matchedIndex]['id'] ?? $sessionId);
}
$sessions[$matchedIndex] = array_merge($sessions[$matchedIndex], [
'id' => $sessionId,
'title' => $title !== '' ? $title : (string) ($sessions[$matchedIndex]['title'] ?? 'Virtual session'),
'meet_room_type' => $roomType,
'meet_room_uuid' => $roomUuid,
'join_url' => $joinUrl !== '' ? $joinUrl : ($sessions[$matchedIndex]['join_url'] ?? null),
'scheduled_at' => $payload['scheduled_at'] ?? $sessions[$matchedIndex]['scheduled_at'] ?? null,
'duration_minutes' => max(15, (int) ($payload['duration_minutes'] ?? ($sessions[$matchedIndex]['duration_minutes'] ?? 60))),
]);
$payloadData = (array) ($event->payload ?? []);
$payloadData['content'] = array_merge($content, ['virtual_sessions' => $sessions]);
$event->payload = $payloadData;
$event->save();
$event = $this->sync->sync($event->fresh(), $owner);
return array_merge($this->summarizeEvent($event), [
'entity_id' => $sessionId,
'meet_room_uuid' => $roomUuid,
'join_url' => $joinUrl,
]);
}
/** @return array<string, mixed> */
private function summarizeEvent(QrCode $event): array
{
$content = $event->content();
$sessions = collect((array) ($content['virtual_sessions'] ?? []))
->filter(fn ($session) => is_array($session))
->map(fn (array $session) => [
'id' => (string) ($session['id'] ?? ''),
'title' => (string) ($session['title'] ?? ''),
'meet_room_type' => (string) ($session['meet_room_type'] ?? 'town_hall'),
'meet_room_uuid' => ($uuid = trim((string) ($session['meet_room_uuid'] ?? ''))) !== '' ? $uuid : null,
'scheduled_at' => $session['scheduled_at'] ?? null,
'duration_minutes' => (int) ($session['duration_minutes'] ?? 60),
])
->values()
->all();
return [
'id' => $event->id,
'title' => (string) ($content['title'] ?? $event->label ?? 'Event'),
'format' => (string) ($content['format'] ?? 'in_person'),
'short_code' => $event->short_code,
'registration_url' => $event->publicUrl(),
'admin_url' => url('/events/'.$event->id.'/attendees'),
'edit_url' => url('/events/'.$event->id),
'virtual_sessions' => $sessions,
];
}
}