a.buynovskiy
Пользователь
-
Зарегистрирован
-
Посещение
Активность репутации
-
a.buynovskiy получил реакцию от AndrewPro в Для изменения одного события в повторяющейся серии с помощью библиотеки `garethp/php-ews`Проверенный код рабочий. другое не пробовал так как времени не было.
<?php namespace App\Modules\Calendar\Actions\OutlookCalendar; use App\Modules\Calendar\Actions\OutlookCalendar\Helpers\Helpers; use App\Modules\Calendar\Models\CalendarEvent; use garethp\ews\API\Type\ItemIdType; use garethp\ews\CalendarAPI; use Illuminate\Support\Facades\Log; class UpdateEventInOutlookCalendarAction { public function __construct( protected GetEwsClientAction $getClientAction, protected PrepareEventForExportAction $prepareEventForExportAction, protected ExportCreateToOutlookCalendarAction $exportCreatedEventAction, ) { } public function __invoke(CalendarEvent $event, string $mode = ''): bool { if (empty($event->uid) || !$event->calendarData) { Log::warning('Missing calendar data or external_id', ['event_id' => $event->id]); return false; } if (strtotime($event->start_date) >= strtotime($event->end_date)) { Log::warning('Invalid time range for event', ['event_id' => $event->id]); return false; } try { $token = ($this->getClientAction)($event->calendarData); $api = ($this->getClientAction)->getApiClient($token['access_token'], $event->createdByData->email); $calendar = $event->calendarData->connection->is_default ? $api->getCalendar() : $api->getCalendar($event->calendarData->name); $eventData = ($this->prepareEventForExportAction)($event, $event->timezone ?? 'UTC', 'update'); if ($event->parent_recurrence_event !== null) { Log::debug('Обновление одного из серии: ', [print_r($eventData, 1)]); $this->updateSingleOccurrence($api, $event, $calendar, $eventData); return true; } $this->updateSeriesOrSingle($calendar, $event, $eventData); return true; } catch (\Throwable $e) { Log::error('UpdateOutlookEventAction failed: ' . $e->getMessage(), [ 'event_id' => $event->id, ]); return false; } } /** * Обновление всей серии или одиночного события * @param CalendarAPI $calendar * @param CalendarEvent $event * @param array $data * @return bool */ protected function updateSeriesOrSingle(CalendarAPI $calendar, CalendarEvent $event, array $data): bool { try { $itemId = new ItemIdType(); $itemId->setId($event->uid); $itemId->setChangeKey($event->change_key_outlook); $calendar->updateCalendarItem( $itemId, $data, ['SendMeetingInvitationsOrCancellations' => 'SendToAllAndSaveCopy'] ); return true; } catch (\Throwable $e) { Log::error('Failed to update Outlook event: ' . $e->getMessage(), [ 'event_id' => $event->id, ]); return false; } } public function updateSingleOccurrence($api, CalendarEvent $event, CalendarAPI $calendar, $updates): bool { try { if (!$event->recurrence_id && !$event->parent_recurrence_event) { throw new \Exception('Событие должно иметь recurrence_id и ссылку на родительскую серию.'); } //получаем конкретное событие для его изменения $itemId = (new Helpers())->getRecurrenceItem($event, $api); if (!$itemId || !$itemId instanceof ItemIdType) { throw new \Exception('Не удалось получить ItemId для обновления экземпляра события.'); } $calendar->updateCalendarItem( $itemId, $updates, ['SendMeetingInvitationsOrCancellations' => 'SendToAllAndSaveCopy'] ); } catch (\Exception $e) { Log::error('Update', [ 'error' => $e->getMessage(), 'event_id' => $event->id ]); } return true; } } И вот вспомогательный метод для получения отдельного вхождения
<?php namespace App\Modules\Calendar\Actions\OutlookCalendar\Helpers; use App\Modules\Calendar\Models\CalendarEvent; use Carbon\Carbon; use garethp\ews\API\Type\ItemIdType; use Illuminate\Support\Facades\Log; class Helpers { public function getRecurrenceItem(CalendarEvent $event, $api): ItemIdType { $itemId = new ItemIdType(); /** @var CalendarEvent $master */ $master = CalendarEvent::find($event->parent_recurrence_event); if (!$master || !$master->uid || !$master->change_key_outlook) { Log::error('Missing parent info for recurrence exception', ['event_id' => $event->id]); return $itemId; } Log::debug('1111 - : '.$this->calculateRecurrenceIndex($master->start_date, $event->recurrence_id, $master->recurrence)); try { $request = [ 'ItemShape' => [ 'BaseShape' => 'IdOnly' ], 'ItemIds' => [ 'OccurrenceItemId' => [ 'RecurringMasterId' => $master->uid, 'InstanceIndex' => $this->calculateRecurrenceIndex($master->start_date, $event->recurrence_id, $master->recurrence) ] ] ]; $response = $api->getClient()->GetItem($request); Log::debug('SOAP request: '.$api->getClient()->getClient()->__getLastRequest()); Log::debug('SOAP request: ', [print_r($response, true)]); $itemId = $response; } catch (\Exception $e) { Log::error('GetItem failed', [ 'error' => $e->getMessage(), 'event_id' => $event->id ]); return $itemId; } return $itemId; } public function calculateRecurrenceIndex(string $startDate, string $occurrenceDate, array $recurrence): ?int { $start = Carbon::parse($startDate)->startOfDay(); $occurrence = Carbon::parse($occurrenceDate)->startOfDay(); $interval = (int)($recurrence['INTERVAL'] ?? 1); if ($occurrence->lessThan($start)) { return null; // Некорректная дата вхождения } switch (strtoupper($recurrence['FREQ'])) { case 'DAILY': return floor($start->diffInDays($occurrence) / $interval) + 1; case 'WEEKLY': $daysOfWeek = array_map(fn($d) => strtoupper($this->dayMap($d)), $recurrence['BYDAY'] ?? []); if (empty($daysOfWeek)) { return null; } $count = 0; $current = $start->copy(); while ($current <= $occurrence) { if (in_array(strtoupper($current->format('l')), $daysOfWeek)) { $count++; } $current->addDay(); if ($current->diffInWeeks($start) % $interval !== 0) { // Пропускаем недели вне интервала continue; } } return $count; case 'MONTHLY': if (!empty($recurrence['BYMONTHDAY'])) { $day = (int)$recurrence['BYMONTHDAY']; $count = 0; $current = $start->copy(); while ($current <= $occurrence) { if ((int)$current->format('d') === $day) { $count++; if ($current->isSameDay($occurrence)) { return $count; } } $current->addMonthNoOverflow($interval); } return null; } break; case 'YEARLY': if (!empty($recurrence['BYMONTH']) && !empty($recurrence['BYMONTHDAY'])) { $count = 0; $current = $start->copy(); while ($current <= $occurrence) { if ( (int)$current->format('m') === (int)$recurrence['BYMONTH'] && (int)$current->format('d') === (int)$recurrence['BYMONTHDAY'] ) { $count++; if ($current->isSameDay($occurrence)) { return $count; } } $current->addYear(); } return null; } break; } return null; // Неподдерживаемый тип или не удалось вычислить } protected function dayMap(string $day): string { return [ 'MO' => 'Monday', 'TU' => 'Tuesday', 'WE' => 'Wednesday', 'TH' => 'Thursday', 'FR' => 'Friday', 'SA' => 'Saturday', 'SU' => 'Sunday', ][strtoupper($day)] ?? 'Monday'; } }