Completed
Push — master ( e22388...d252f0 )
by
unknown
22s queued 11s
created

Calendar::getEndDate()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 17

Duplication

Lines 17
Ratio 100 %

Importance

Changes 0
Metric Value
dl 17
loc 17
rs 9.3888
c 0
b 0
f 0
cc 5
nc 4
nop 0
1
<?php
2
3
namespace CultuurNet\UDB3;
4
5
use Broadway\Serializer\SerializableInterface;
6
use CultuurNet\UDB3\Calendar\OpeningHour;
7
use CultuurNet\UDB3\Model\ValueObject\Calendar\Calendar as Udb3ModelCalendar;
8
use CultuurNet\UDB3\Model\ValueObject\Calendar\CalendarWithDateRange;
9
use CultuurNet\UDB3\Model\ValueObject\Calendar\CalendarWithOpeningHours;
10
use CultuurNet\UDB3\Model\ValueObject\Calendar\CalendarWithSubEvents;
11
use CultuurNet\UDB3\Model\ValueObject\Calendar\DateRange;
12
use CultuurNet\UDB3\Model\ValueObject\Calendar\OpeningHours\OpeningHour as Udb3ModelOpeningHour;
13
use DateTime;
14
use DateTimeInterface;
15
use DateTimeZone;
16
use InvalidArgumentException;
17
18
/**
19
 * Calendar for events and places.
20
 * @todo Replace by CultuurNet\UDB3\Model\ValueObject\Calendar\Calendar.
21
 */
22
class Calendar implements CalendarInterface, JsonLdSerializableInterface, SerializableInterface
23
{
24
25
    /**
26
     * @var CalendarType
27
     */
28
    protected $type = null;
29
30
    /**
31
     * @var DateTimeInterface
32
     */
33
    protected $startDate = null;
34
35
    /**
36
     * @var DateTimeInterface
37
     */
38
    protected $endDate = null;
39
40
    /**
41
     * @var Timestamp[]
42
     */
43
    protected $timestamps = array();
44
45
    /**
46
     * @var OpeningHour[]
47
     */
48
    protected $openingHours = array();
49
50
    /**
51
     * @param CalendarType $type
52
     * @param DateTimeInterface|null $startDate
53
     * @param DateTimeInterface|null $endDate
54
     * @param Timestamp[] $timestamps
55
     * @param OpeningHour[] $openingHours
56
     */
57
    public function __construct(
58
        CalendarType $type,
59
        DateTimeInterface $startDate = null,
60
        DateTimeInterface $endDate = null,
61
        array $timestamps = array(),
62
        array $openingHours = array()
63
    ) {
64
        if (($type->is(CalendarType::SINGLE()) || $type->is(CalendarType::MULTIPLE())) && (empty($timestamps))) {
65
            throw new \UnexpectedValueException('A single or multiple calendar should have timestamps.');
66
        }
67
68
        if ($type->is(CalendarType::PERIODIC()) && (empty($startDate) || empty($endDate))) {
69
            throw new \UnexpectedValueException('A period should have a start- and end-date.');
70
        }
71
72
        foreach ($timestamps as $timestamp) {
73
            if (!is_a($timestamp, Timestamp::class)) {
74
                throw new \InvalidArgumentException('Timestamps should have type TimeStamp.');
75
            }
76
        }
77
78
        foreach ($openingHours as $openingHour) {
79
            if (!is_a($openingHour, OpeningHour::class)) {
80
                throw new \InvalidArgumentException('OpeningHours should have type OpeningHour.');
81
            }
82
        }
83
84
        $this->type = $type->toNative();
85
        $this->startDate = $startDate;
86
        $this->endDate = $endDate;
87
        $this->openingHours = $openingHours;
88
89
        usort($timestamps, function (Timestamp $timestamp, Timestamp $otherTimestamp) {
90
            return $timestamp->getStartDate() <=> $otherTimestamp->getStartDate();
91
        });
92
93
        $this->timestamps = $timestamps;
94
95
    }
96
97
    /**
98
     * @inheritdoc
99
     */
100
    public function getType()
101
    {
102
        return CalendarType::fromNative($this->type);
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108
    public function serialize()
109
    {
110
        $serializedTimestamps = array_map(
111
            function (Timestamp $timestamp) {
112
                return $timestamp->serialize();
113
            },
114
            $this->timestamps
115
        );
116
117
        $serializedOpeningHours = array_map(
118
            function (OpeningHour $openingHour) {
119
                return $openingHour->serialize();
120
            },
121
            $this->openingHours
122
        );
123
124
        $calendar = [
125
            'type' => $this->type,
126
        ];
127
128
        empty($this->startDate) ?: $calendar['startDate'] = $this->startDate->format(DateTime::ATOM);
129
        empty($this->endDate) ?: $calendar['endDate'] = $this->endDate->format(DateTime::ATOM);
130
        empty($serializedTimestamps) ?: $calendar['timestamps'] = $serializedTimestamps;
131
        empty($serializedOpeningHours) ?: $calendar['openingHours'] = $serializedOpeningHours;
132
133
        return $calendar;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139
    public static function deserialize(array $data)
140
    {
141
        $calendarType = CalendarType::fromNative($data['type']);
142
143
        // Backwards compatibility for serialized single or multiple calendar types that are missing timestamps but do
144
        // have a start and end date.
145
        $defaultTimeStamps = [];
146
        if ($calendarType->sameValueAs(CalendarType::SINGLE()) || $calendarType->sameValueAs(CalendarType::MULTIPLE())) {
147
            $defaultTimeStampStartDate = !empty($data['startDate']) ? self::deserializeDateTime($data['startDate']) : null;
148
            $defaultTimeStampEndDate = !empty($data['endDate']) ? self::deserializeDateTime($data['endDate']) : $defaultTimeStampStartDate;
149
            $defaultTimeStamp = $defaultTimeStampStartDate && $defaultTimeStampEndDate ? new Timestamp($defaultTimeStampStartDate, $defaultTimeStampEndDate) : null;
150
            $defaultTimeStamps = $defaultTimeStamp ? [$defaultTimeStamp] : [];
151
        }
152
153
        return new static(
154
            $calendarType,
155
            !empty($data['startDate']) ? self::deserializeDateTime($data['startDate']) : null,
156
            !empty($data['endDate']) ? self::deserializeDateTime($data['endDate']) : null,
157
            !empty($data['timestamps']) ? array_map(
158
                function ($timestamp) {
159
                    return Timestamp::deserialize($timestamp);
160
                },
161
                $data['timestamps']
162
            ) : $defaultTimeStamps,
163
            !empty($data['openingHours']) ? array_map(
164
                function ($openingHour) {
165
                    return OpeningHour::deserialize($openingHour);
166
                },
167
                $data['openingHours']
168
            ) : []
169
        );
170
    }
171
172
    /**
173
     * This deserialization function takes into account old data that might be missing a timezone.
174
     * It will fall back to creating a DateTime object and assume Brussels.
175
     * If this still fails an error will be thrown.
176
     *
177
     * @param $dateTimeData
178
     * @return DateTime
179
     *
180
     * @throws InvalidArgumentException
181
     */
182
    private static function deserializeDateTime($dateTimeData)
183
    {
184
        $dateTime = DateTime::createFromFormat(DateTime::ATOM, $dateTimeData);
185
186
        if ($dateTime === false) {
187
            $dateTime = DateTime::createFromFormat('Y-m-d\TH:i:s', $dateTimeData, new DateTimeZone('Europe/Brussels'));
188
189
            if (!$dateTime) {
190
                throw new InvalidArgumentException('Invalid date string provided for timestamp, ISO8601 expected!');
191
            }
192
        }
193
194
        return $dateTime;
195
    }
196
197
    /**
198
     * @inheritdoc
199
     */
200 View Code Duplication
    public function getStartDate()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
    {
202
        $timestamps = $this->getTimestamps();
203
204
        if (empty($timestamps)) {
205
            return $this->startDate;
206
        }
207
208
        $startDate = null;
209
        foreach ($timestamps as $timestamp) {
210
            if ($startDate === null || $timestamp->getStartDate() < $startDate) {
211
                $startDate = $timestamp->getStartDate();
212
            }
213
        }
214
215
        return $startDate;
216
    }
217
218
    /**
219
     * @inheritdoc
220
     */
221 View Code Duplication
    public function getEndDate()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
222
    {
223
        $timestamps = $this->getTimestamps();
224
225
        if (empty($timestamps)) {
226
            return $this->endDate;
227
        }
228
229
        $endDate = null;
230
        foreach ($this->getTimestamps() as $timestamp) {
231
            if ($endDate === null || $timestamp->getEndDate() > $endDate) {
232
                $endDate = $timestamp->getEndDate();
233
            }
234
        }
235
236
        return $endDate;
237
    }
238
239
    /**
240
     * @inheritdoc
241
     */
242
    public function getOpeningHours()
243
    {
244
        return $this->openingHours;
245
    }
246
247
    /**
248
     * @inheritdoc
249
     */
250
    public function getTimestamps()
251
    {
252
        return $this->timestamps;
253
    }
254
255
    /**
256
     * Return the jsonLD version of a calendar.
257
     */
258
    public function toJsonLd()
259
    {
260
        $jsonLd = [];
261
262
        $jsonLd['calendarType'] = $this->getType()->toNative();
263
264
        $startDate = $this->getStartDate();
265
        $endDate = $this->getEndDate();
266
        if ($startDate !== null) {
267
            $jsonLd['startDate'] = $startDate->format(DateTime::ATOM);
268
        }
269
        if ($endDate !== null) {
270
            $jsonLd['endDate'] = $endDate->format(DateTime::ATOM);
271
        }
272
273
        $timestamps = $this->getTimestamps();
274
        if (!empty($timestamps)) {
275
            $jsonLd['subEvent'] = array();
276
            foreach ($timestamps as $timestamp) {
277
                $jsonLd['subEvent'][] = array(
278
                    '@type' => 'Event',
279
                    'startDate' => $timestamp->getStartDate()->format(DateTime::ATOM),
280
                    'endDate' => $timestamp->getEndDate()->format(DateTime::ATOM),
281
                );
282
            }
283
        }
284
285
        $openingHours = $this->getOpeningHours();
286
        if (!empty($openingHours)) {
287
            $jsonLd['openingHours'] = array();
288
            foreach ($openingHours as $openingHour) {
289
                $jsonLd['openingHours'][] = $openingHour->serialize();
290
            }
291
        }
292
293
        return $jsonLd;
294
    }
295
296
    /**
297
     * @param Calendar $otherCalendar
298
     * @return bool
299
     */
300
    public function sameAs(Calendar $otherCalendar)
301
    {
302
        return $this->toJsonLd() == $otherCalendar->toJsonLd();
303
    }
304
305
    /**
306
     * @param Udb3ModelCalendar $calendar
307
     * @return self
308
     */
309
    public static function fromUdb3ModelCalendar(Udb3ModelCalendar $calendar)
310
    {
311
        $type = CalendarType::fromNative($calendar->getType()->toString());
312
313
        $startDate = null;
314
        $endDate = null;
315
        $timestamps = [];
316
        $openingHours = [];
317
318
        if ($calendar instanceof CalendarWithDateRange) {
319
            $startDate = $calendar->getStartDate();
320
            $endDate = $calendar->getEndDate();
321
        }
322
323
        if ($calendar instanceof CalendarWithSubEvents) {
324
            $timestamps = array_map(
325
                function (DateRange $dateRange) {
326
                    return Timestamp::fromUdb3ModelDateRange($dateRange);
327
                },
328
                $calendar->getSubEvents()->toArray()
329
            );
330
        }
331
332
        if ($calendar instanceof CalendarWithOpeningHours) {
333
            $openingHours = array_map(
334
                function (Udb3ModelOpeningHour $openingHour) {
335
                    return OpeningHour::fromUdb3ModelOpeningHour($openingHour);
336
                },
337
                $calendar->getOpeningHours()->toArray()
338
            );
339
        }
340
341
        return new self($type, $startDate, $endDate, $timestamps, $openingHours);
342
    }
343
}
344