Test Failed
Push — main ( e66c61...e8516d )
by Davide
13:44
created

EventService::decodeOnMonthlyKind()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 41
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 5

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 5
eloc 35
c 2
b 0
f 0
nc 5
nop 1
dl 0
loc 41
ccs 25
cts 25
cp 1
crap 5
rs 9.0488
1
<?php
2
3
namespace App\Services;
4
5
use App\Helpers\DateHelpers;
6
use App\Helpers\Helper;
7
use App\Helpers\ImageHelpers;
8
use App\Http\Requests\EventSearchRequest;
9
use App\Http\Requests\EventStoreRequest;
10
use App\Models\Event;
11
use App\Models\EventRepetition;
12
use App\Notifications\ExpiringEventMailNotification;
13
use App\Repositories\EventRepository;
14
use Carbon\Carbon;
15
use DateTime;
16
use Illuminate\Http\Request;
17
use Illuminate\Support\Collection;
18
use Illuminate\Support\Facades\Cache;
19
use Illuminate\Support\Facades\Log;
20
use PHPUnit\TextUI\Help;
21
22
class EventService
23
{
24
    private EventRepository $eventRepository;
25
    private EventRepetitionService $eventRepetitionService;
26
27
    /**
28
     * EventService constructor.
29 22
     *
30
     * @param \App\Repositories\EventRepository $eventRepository
31
     * @param \App\Services\EventRepetitionService $eventRepetitionService
32
     */
33 22
    public function __construct(
34 22
        EventRepository $eventRepository,
35 22
        EventRepetitionService $eventRepetitionService
36
    ) {
37
        $this->eventRepository = $eventRepository;
38
        $this->eventRepetitionService = $eventRepetitionService;
39
    }
40
41
    /**
42
     * Create a event
43
     *
44
     * @param \App\Http\Requests\EventStoreRequest $request
45
     *
46
     * @return \App\Models\Event
47 4
     * @throws \Spatie\ModelStatus\Exceptions\InvalidStatus
48
     */
49 4
    public function createEvent(EventStoreRequest $request): Event
50 4
    {
51
        $event = $this->eventRepository->store($request->all());
52 4
        ImageHelpers::storeImages($event, $request, 'introimage');
53
54 4
        $this->eventRepetitionService->updateEventRepetitions($request->all(), $event->id);
55
56 4
        $event->setStatus('published');
57
        $this->cleanActiveEventsCaches();
58
59
        return $event;
60
    }
61
62
    /**
63
     * Update the Event
64
     *
65
     * @param \App\Http\Requests\EventStoreRequest $request
66
     * @param int $eventId
67
     *
68
     * @return \App\Models\Event
69
     */
70 1
71
    public function updateEvent(EventStoreRequest $request, int $eventId): Event
72 1
    {
73
        $event = $this->eventRepository->update($request->all(), $eventId);
74 1
75 1
        ImageHelpers::storeImages($event, $request, 'introimage');
76
        ImageHelpers::deleteImages($event, $request, 'introimage');
77 1
78
        $this->eventRepetitionService->updateEventRepetitions($request->all(), $eventId);
79 1
        $this->cleanActiveEventsCaches();
80
81
        return $event;
82
    }
83
84
    /**
85
     * Return the event from the database
86
     *
87
     * @param int $eventId
88
     *
89 1
     * @return \App\Models\Event
90
     */
91 1
    public function getById(int $eventId): Event
92
    {
93
        return $this->eventRepository->getById($eventId);
94
    }
95
96
    /**
97
     * Get all the Events.
98
     *
99
     * @param int|null $recordsPerPage
100
     * @param array|null $searchParameters
101
     *
102 1
     * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Contracts\Pagination\LengthAwarePaginator
103
     */
104 1
    public function getEvents(int $recordsPerPage = null, array $searchParameters = null)
105
    {
106
        return $this->eventRepository->getAll($recordsPerPage, $searchParameters);
107
    }
108
109
    /**
110
     * Delete the event from the database
111
     *
112 1
     * @param int $eventId
113
     */
114 1
    public function deleteEvent(int $eventId): void
115 1
    {
116
        $this->eventRepository->delete($eventId);
117
        $this->cleanActiveEventsCaches();
118
    }
119
120
    /**
121
     * Get the number of event created in the last 30 days.
122 1
     *
123
     * @return int
124 1
     */
125
    public function getNumberEventsCreatedLastThirtyDays(): int
126
    {
127
        return Event::whereDate('created_at', '>', date('Y-m-d', strtotime('-30 days')))->count();
128
    }
129
130
    /**
131
     * Return an array with the event data related to:
132
     * - date and time start
133
     * - date and time end
134
     * - repeat until
135
     * - multiple dates
136
     *
137
     * @param Event $event
138
     * @param EventRepetition $eventFirstRepetition
139 1
     *
140
     * @return array
141 1
     */
142 1
    public function getEventDateTimeParameters(Event $event, EventRepetition $eventFirstRepetition): array
143 1
    {
144
        $dateTime = [];
145
        $dateTime['dateStart'] = (isset($eventFirstRepetition->start_repeat)) ? date('d/m/Y', strtotime($eventFirstRepetition->start_repeat)) : '';
146
        $dateTime['dateEnd'] = (isset($eventFirstRepetition->end_repeat)) ? date('d/m/Y', strtotime($eventFirstRepetition->end_repeat)) : '';
147
148 1
        //$dateTime['timeStart'] = (isset($eventFirstRepetition->start_repeat)) ? date('g:i A', strtotime($eventFirstRepetition->start_repeat)) : '';
149 1
        //$dateTime['timeEnd'] = (isset($eventFirstRepetition->end_repeat)) ? date('g:i A', strtotime($eventFirstRepetition->end_repeat)) : '';
150 1
151 1
        $dateTime['timeStartHours'] = (isset($eventFirstRepetition->start_repeat)) ? date('g', strtotime($eventFirstRepetition->start_repeat)) : '';
152 1
        $dateTime['timeStartMinutes'] = (isset($eventFirstRepetition->start_repeat)) ? date('i', strtotime($eventFirstRepetition->start_repeat)) : '';
153 1
        $dateTime['timeStartAmpm'] = (isset($eventFirstRepetition->start_repeat)) ? date('A', strtotime($eventFirstRepetition->start_repeat)) : '';
154
        $dateTime['timeEndHours'] = (isset($eventFirstRepetition->end_repeat)) ? date('g', strtotime($eventFirstRepetition->end_repeat)) : '';
155 1
        $dateTime['timeEndMinutes'] = (isset($eventFirstRepetition->end_repeat)) ? date('i', strtotime($eventFirstRepetition->end_repeat)) : '';
156 1
        $dateTime['timeEndAmpm'] = (isset($eventFirstRepetition->end_repeat)) ? date('A', strtotime($eventFirstRepetition->end_repeat)) : '';
157
158 1
        $dateTime['repeatUntil'] = is_null($event->repeat_until) ? null : date('d/m/Y', strtotime($event->repeat_until));
159
        $dateTime['multipleDates'] = $event->multiple_dates;
160
161
        return $dateTime;
162
    }
163
164
    /**
165
     * Return the HTML of the monthly select dropdown - inspired by - https://www.theindychannel.com/calendar
166
     * - Used by the AJAX in the event repeat view -
167
     * - The HTML contain a <select></select> with four <options></options>.
168
     *
169
     * @param string $date
170 1
     *
171
     * @return string
172 1
     */
173 1
    public function getMonthlySelectOptions(string $date): string
174 1
    {
175 1
        $monthlySelectOptions = [];
176
        $date = implode('-', array_reverse(explode('/', $date)));  // Our YYYY-MM-DD date string
177
        $unixTimestamp = strtotime($date);  // Convert the date string into a unix timestamp.
178
        $dayOfWeekString = date('l', $unixTimestamp); // Monday | Tuesday | Wednesday | ..
179 1
180 1
        // Add option: Same day number.
181
        // eg. "the 28th day of the month"
182 1
        $dateArray = explode('-', $date);
183 1
        $dayNumber = ltrim($dateArray[2], '0'); // remove the 0 in front of a day number eg. 02/10/2018
184
185 1
        $format = __('ordinalDays.the_' . ($dayNumber) . '_x_of_the_month');
186 1
        $repeatText = sprintf($format, 'day');
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type array and array; however, parameter $format of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

186
        $repeatText = sprintf(/** @scrutinizer ignore-type */ $format, 'day');
Loading history...
187 1
188
        array_push($monthlySelectOptions, [
189
            'value' => '0|' . $dayNumber,
190
            'text' => $repeatText,
191
        ]);
192 1
193 1
        // Add option: Same weekday/week of the month.
194
        // eg. the "1st Monday" 1|1|1 (first week, monday)
195 1
        $dayOfWeekValue = date('N', $unixTimestamp); // 1 (for Monday) through 7 (for Sunday)
196 1
        $weekOfTheMonth = DateHelpers::monthWeekNumber($date, $dayOfWeekValue); // 1 | 2 | 3 | 4 | 5
197
198 1
        $format = __('ordinalDays.the_' . ($weekOfTheMonth) . '_x_of_the_month');
199 1
        $repeatText = sprintf($format, $dayOfWeekString);
200 1
201
        array_push($monthlySelectOptions, [
202
            'value' => '1|' . $weekOfTheMonth . '|' . $dayOfWeekValue,
203
            'text' => $repeatText,
204
        ]);
205 1
206
        // Add option: Same day of the month (from the end).
207 1
        // eg. "the 3rd to last day of the month" (0 if last day, 1 if 2nd to last day, , 2 if 3rd to last day)
208 1
        $dayOfMonthFromTheEnd = DateHelpers::dayOfMonthFromTheEnd($unixTimestamp); // 1 | 2 | 3 | 4 | 5
209
210 1
        $format = __('ordinalDays.the_'.($dayOfMonthFromTheEnd + 1).'_to_last_x_of_the_month');
211 1
        $repeatText = sprintf($format, 'day');
212 1
213
        array_push($monthlySelectOptions, [
214
            'value' => '2|'.$dayOfMonthFromTheEnd,
215
            'text' => $repeatText,
216
        ]);
217 1
218
        // Add option: Same weekday/week of the month (from the end).
219 1
        // eg. the last Friday - (0 if last Friday, 1 if the 2nd to last Friday, 2 if the 3nd to last Friday)
220
        $monthWeekNumberFromTheEnd = DateHelpers::monthWeekNumberFromTheEnd($unixTimestamp); // 1 | 2 | 3 | 4 | 5
221
222 1
        if ($monthWeekNumberFromTheEnd == 1) {
223
            $weekValue = 0;
224
        } else {
225 1
            $weekValue = $monthWeekNumberFromTheEnd - 1;
226 1
        }
227
228 1
        $format = __('ordinalDays.the_' . ($monthWeekNumberFromTheEnd) . '_to_last_x_of_the_month');
229 1
        $repeatText = sprintf($format, $dayOfWeekString);
230 1
231
        array_push($monthlySelectOptions, [
232
            'value' => '3|' . $weekValue . '|' . $dayOfWeekValue,
233
            'text' => $repeatText,
234 1
        ]);
235 1
236 1
        // GENERATE the HTML to return
237 1
        $selectTitle = __('general.select_repeat_monthly_kind');
238
        $onMonthlyKindSelect = "<select name='on_monthly_kind' id='on_monthly_kind' class='selectpicker' title='".$selectTitle."'>";
0 ignored issues
show
Bug introduced by
Are you sure $selectTitle of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

238
        $onMonthlyKindSelect = "<select name='on_monthly_kind' id='on_monthly_kind' class='selectpicker' title='"./** @scrutinizer ignore-type */ $selectTitle."'>";
Loading history...
239 1
        foreach ($monthlySelectOptions as $key => $monthlySelectOption) {
240
            $onMonthlyKindSelect .= "<option value='".$monthlySelectOption['value']."'>".$monthlySelectOption['text'].'</option>';
241 1
        }
242
        $onMonthlyKindSelect .= '</select>';
243
244
        return $onMonthlyKindSelect;
245
    }
246
247
    /**
248
     * Return a string that describe repetition kind in the event show view.
249
     *
250
     * @param \App\Models\Event $event
251
     * @param \App\Models\EventRepetition $firstRpDates
252 4
     *
253
     * @return string $ret
254 4
     * @throws \Exception
255
     */
256 4
    public static function getRepetitionTextString(Event $event, EventRepetition $firstRpDates): string
257 4
    {
258 1
        $ret = '';
259 3
260 1
        switch ($event->repeat_type) {
261
            case '1': // noRepeat
262
                $dateStart = date('d/m/Y', strtotime($firstRpDates->start_repeat));
263 1
                $dateEnd = date('d/m/Y', strtotime($firstRpDates->end_repeat));
264
265 1
                if ($dateStart == $dateEnd) {
266 1
                    $ret = $dateStart;
267 1
                } else {
268
                    $ret = "From {$dateStart} to {$dateEnd}";
269
                }
270 1
                break;
271
            case '2': // repeatWeekly
272
                $repeatUntil = new DateTime($event->repeat_until);
273 1
274 1
                // Get the name of the weekly day when the event repeat, if two days, return like "Thursday and Sunday"
275 1
                $repetitonWeekdayNumbersArray = explode(',', $event->repeat_weekly_on);
276 2
277 1
                $repetitonWeekdayNamesArray = [];
278 1
                foreach ($repetitonWeekdayNumbersArray as $key => $repetitonWeekdayNumber) {
279
                    $repetitonWeekdayNamesArray[] = DateHelpers::decodeRepeatWeeklyOn($repetitonWeekdayNumber);
280
                }
281 1
                // create from an array a string with all the values divided by " and "
282 1
                $nameOfTheRepetitionWeekDays = implode(' and ', $repetitonWeekdayNamesArray);
283 1
284 1
                //$ret = 'The event happens every '.$nameOfTheRepetitionWeekDays.' until '.$repeatUntil->format('d/m/Y');
285 1
                $format = __('event.the_event_happens_every_x_until_x');
286 1
                $ret .= sprintf($format, $nameOfTheRepetitionWeekDays, $repeatUntil->format('d/m/Y'));
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type array and array; however, parameter $format of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

286
                $ret .= sprintf(/** @scrutinizer ignore-type */ $format, $nameOfTheRepetitionWeekDays, $repeatUntil->format('d/m/Y'));
Loading history...
287
                break;
288
            case '3': //repeatMonthly
289 1
                $repeatUntil = new DateTime($event->repeat_until);
290 1
                $repetitionFrequency = self::decodeOnMonthlyKind($event->on_monthly_kind);
291 1
292
                //$ret = 'The event happens '.$repetitionFrequency.' until '.$repeatUntil->format('d/m/Y');
293 1
                $format = __('event.the_event_happens_x_until_x');
294 1
                $ret .= sprintf($format, $repetitionFrequency, $repeatUntil->format('d/m/Y'));
295
                break;
296 1
            case '4': //repeatMultipleDays
297 1
                $dateStart = date('d/m/Y', strtotime($firstRpDates->start_repeat));
298 1
                $singleDaysRepeatDatas = explode(',', $event->multiple_dates);
299 1
300
                // Sort the datas
301
                usort($singleDaysRepeatDatas, function ($a, $b) {
302 4
                    $a = Carbon::createFromFormat('d/m/Y', $a);
303
                    $b = Carbon::createFromFormat('d/m/Y', $b);
304
305
                    return strtotime($a) - strtotime($b);
0 ignored issues
show
Bug introduced by
It seems like $a can also be of type false; however, parameter $datetime of strtotime() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

305
                    return strtotime(/** @scrutinizer ignore-type */ $a) - strtotime($b);
Loading history...
306
                });
307
308
                $ret .= __('event.the_event_happens_on_this_dates');
309
                $ret .= $dateStart . ', ';
310
                $ret .= Helper::getStringFromArraySeparatedByComma($singleDaysRepeatDatas);
311 1
                break;
312
        }
313 1
314
        return $ret;
315 1
    }
316 1
317 1
    /**
318
     * Return a string that describe the report misuse reason.
319
     *
320
     * @param  int $reason
321
     * @return string $ret
322
     */
323
    public static function getReportMisuseReasonDescription(int $reason): string
324
    {
325
        $ret = '';
326
        switch ($reason) {
327
            case '1':
328
                $ret = 'Not about Contact Improvisation';
329 1
                break;
330
            case '2':
331
                $ret = 'Contains wrong information';
332
                break;
333
            case '3':
334
                $ret = 'It is not translated in english';
335
                break;
336
            case '4':
337
                $ret = 'Other (specify in the message)';
338
                break;
339 5
        }
340
341 5
        return $ret;
342 5
    }
343
344 5
    /**
345 5
     * Decode the event on_monthly_kind field - used in event.show.
346 5
     * Return a string like "the 4th to last Thursday of the month".
347 5
     *
348 5
     * @param  string $onMonthlyKindCode
349 5
     * @return string
350 5
     */
351 5
    public static function decodeOnMonthlyKind(string $onMonthlyKindCode): string
352
    {
353
        $ret = '';
354
        $onMonthlyKindCodeArray = explode('|', $onMonthlyKindCode);
355 5
        $weekDays = [
356 5
            '',
357 1
            __('general.monday'),
358 1
            __('general.tuesday'),
359 1
            __('general.wednesday'),
360 1
            __('general.thursday'),
361 4
            __('general.friday'),
362 2
            __('general.saturday'),
363 2
            __('general.sunday'),
364 2
        ];
365 2
366 2
        switch ($onMonthlyKindCodeArray[0]) {
367 2
            case '0':  // 0|7 eg. the 7th day of the month
368 1
                $dayNumber = $onMonthlyKindCodeArray[1];
369 1
                $format = __("ordinalDays.the_{$dayNumber}_x_of_the_month");
370 1
                $ret = sprintf($format, __('general.day'));
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type array and array; however, parameter $format of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

370
                $ret = sprintf(/** @scrutinizer ignore-type */ $format, __('general.day'));
Loading history...
Bug introduced by
It seems like __('general.day') can also be of type array and array; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

370
                $ret = sprintf($format, /** @scrutinizer ignore-type */ __('general.day'));
Loading history...
371 1
                break;
372 1
            case '1':  // 1|2|4 eg. the 2nd Thursday of the month
373 1
                $dayNumber = $onMonthlyKindCodeArray[1];
374 1
                $weekDay = $weekDays[$onMonthlyKindCodeArray[2]]; // Monday, Tuesday, Wednesday
375 1
                $format = __("ordinalDays.the_{$dayNumber}_x_of_the_month");
376 1
                $ret = sprintf($format, $weekDay);
377 1
                break;
378
            case '2': // 2|20 eg. the 21st to last day of the month
379
                $dayNumber = (int) $onMonthlyKindCodeArray[1] + 1;
380 5
                $format = __("ordinalDays.the_{$dayNumber}_to_last_x_of_the_month");
381
                $ret = sprintf($format, __('general.day'));
382
                break;
383
            case '3': // 3|3|4 eg. the 4th to last Thursday of the month
384
                $dayNumber = (int) $onMonthlyKindCodeArray[1] + 1;
385
                $weekDay = $weekDays[$onMonthlyKindCodeArray[2]]; // Monday, Tuesday, Wednesday
386
                $format = __("ordinalDays.the_{$dayNumber}_to_last_x_of_the_month");
387
                $ret = sprintf($format, $weekDay);
388
                break;
389
        }
390
391
        return $ret;
392
    }
393
394
    /**
395
     * Return the list of the expiring repetitive events (the 7th day from now).
396
     * When the cache parameter is true, get them from the cache.
397
     *
398
     * The cache tag get invalidated once a day and also on event save, update and delete.
399
     * Using the function cleanActiveEventsCaches()
400
     * To empty the cache you can run a 'php artisan cache:clear'
401
     *
402
     * @param bool $cached
403
     *
404
     * @return Collection
405
     */
406
    public function getRepetitiveEventsExpiringInOneWeek(bool $cached): Collection
407
    {
408
        if (!$cached) {
409
            return $this->eventRepository->getRepetitiveExpiringInOneWeek();
410
        }
411
412
        $cacheTag = 'active_events';
413
        $seconds = 86400; // One day
414
        return Cache::remember($cacheTag, $seconds, function () {
415
            return $this->eventRepository->getRepetitiveExpiringInOneWeek();
416
        });
417
    }
418
419
    /**
420
     * Send an email to the users which repetitive events are expiring.
421
     *
422
     * @return string
423
     */
424
    public function sendEmailToExpiringEventsOrganizers(): string
425
    {
426
        $data = [];
427
        $data['emailFrom'] = env('ADMIN_MAIL');
428
        $data['senderName'] = 'CI Global Calendar Administrator';
429
430
        $expiringRepetitiveEvents = self::getRepetitiveEventsExpiringInOneWeek(true);
0 ignored issues
show
Bug Best Practice introduced by
The method App\Services\EventServic...entsExpiringInOneWeek() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

430
        /** @scrutinizer ignore-call */ 
431
        $expiringRepetitiveEvents = self::getRepetitiveEventsExpiringInOneWeek(true);
Loading history...
431
        foreach ($expiringRepetitiveEvents as $key => $event) {
432
            $event->user->notify(new ExpiringEventMailNotification($data, $event));
433
        }
434
435
        $message = empty($expiringRepetitiveEvents) ?
436
            'No events were expiring'
437
            : count($expiringRepetitiveEvents) . ' events were expiring, mails sent to the organizers.';
438
439
        Log::notice($message);
440
        return $message;
441
    }
442
443
    /**
444
     * Invalidate the cache tags related to active events.
445
     *
446
     * @return void
447
     */
448
    public function cleanActiveEventsCaches(): void
449
    {
450
        Cache::forget('active_events');
451
        //Cache::forget('active_events_map_markers_json');
452
        //Cache::forget('active_events_map_markers_db_data');
453
    }
454
455
456
457
}
458