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

EventService::getMonthlySelectOptions()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 72
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 3.0001

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 40
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 72
ccs 37
cts 38
cp 0.9737
crap 3.0001
rs 9.28

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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