Completed
Push — master ( 1cfe39...a6a69c )
by Jonas
15s queued 11s
created

Calendar::__construct()   B

Complexity

Conditions 11
Paths 9

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 7.3166
c 0
b 0
f 0
cc 11
nc 9
nop 5

How to fix   Complexity   

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 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::MULTIPLE()) || $type->is(CalendarType::SINGLE())) && empty($startDate)) {
65
            throw new \UnexpectedValueException('Start date can not be empty for calendar type: ' . $type . '.');
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->timestamps = $timestamps;
88
        $this->openingHours = $openingHours;
89
    }
90
91
    /**
92
     * @inheritdoc
93
     */
94
    public function getType()
95
    {
96
        return CalendarType::fromNative($this->type);
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function serialize()
103
    {
104
        $serializedTimestamps = array_map(
105
            function (Timestamp $timestamp) {
106
                return $timestamp->serialize();
107
            },
108
            $this->timestamps
109
        );
110
111
        $serializedOpeningHours = array_map(
112
            function (OpeningHour $openingHour) {
113
                return $openingHour->serialize();
114
            },
115
            $this->openingHours
116
        );
117
118
        $calendar = [
119
          'type' => $this->type,
120
        ];
121
122
        empty($this->startDate) ?: $calendar['startDate'] = $this->startDate->format(DateTime::ATOM);
123
        empty($this->endDate) ?: $calendar['endDate'] = $this->endDate->format(DateTime::ATOM);
124
        empty($serializedTimestamps) ?: $calendar['timestamps'] = $serializedTimestamps;
125
        empty($serializedOpeningHours) ?: $calendar['openingHours'] = $serializedOpeningHours;
126
127
        return $calendar;
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public static function deserialize(array $data)
134
    {
135
        return new static(
136
            CalendarType::fromNative($data['type']),
137
            !empty($data['startDate']) ? self::deserializeDateTime($data['startDate']) : null,
138
            !empty($data['endDate']) ? self::deserializeDateTime($data['endDate']) : null,
139
            !empty($data['timestamps']) ? array_map(
140
                function ($timestamp) {
141
                    return Timestamp::deserialize($timestamp);
142
                },
143
                $data['timestamps']
144
            ) : [],
145
            !empty($data['openingHours']) ? array_map(
146
                function ($openingHour) {
147
                    return OpeningHour::deserialize($openingHour);
148
                },
149
                $data['openingHours']
150
            ) : []
151
        );
152
    }
153
154
    /**
155
     * This deserialization function takes into account old data that might be missing a timezone.
156
     * It will fall back to creating a DateTime object and assume Brussels.
157
     * If this still fails an error will be thrown.
158
     *
159
     * @param $dateTimeData
160
     * @return DateTime
161
     *
162
     * @throws InvalidArgumentException
163
     */
164
    private static function deserializeDateTime($dateTimeData)
165
    {
166
        $dateTime = DateTime::createFromFormat(DateTime::ATOM, $dateTimeData);
167
168
        if ($dateTime === false) {
169
            $dateTime = DateTime::createFromFormat('Y-m-d\TH:i:s', $dateTimeData, new DateTimeZone('Europe/Brussels'));
170
171
            if (!$dateTime) {
172
                throw new InvalidArgumentException('Invalid date string provided for timestamp, ISO8601 expected!');
173
            }
174
        }
175
176
        return $dateTime;
177
    }
178
179
    /**
180
     * @inheritdoc
181
     */
182
    public function getStartDate()
183
    {
184
        if (empty($this->getTimestamps())) {
185
            return $this->startDate;
186
        }
187
188
        $firstStartDate = $this->startDate;
189
        foreach ($this->getTimestamps() as $timestamp) {
190
            if ($timestamp->getStartDate() < $firstStartDate) {
191
                $firstStartDate = $timestamp->getStartDate();
192
            }
193
        }
194
195
        return $firstStartDate;
196
    }
197
198
    /**
199
     * @inheritdoc
200
     */
201
    public function getEndDate()
202
    {
203
        if (empty($this->getTimestamps())) {
204
            return $this->endDate;
205
        }
206
207
        $lastEndDate = $this->endDate;
208
        foreach ($this->getTimestamps() as $timestamp) {
209
            if ($timestamp->getEndDate() > $lastEndDate) {
210
                $lastEndDate = $timestamp->getEndDate();
211
            }
212
        }
213
214
        return $lastEndDate;
215
    }
216
217
    /**
218
     * @inheritdoc
219
     */
220
    public function getOpeningHours()
221
    {
222
        return $this->openingHours;
223
    }
224
225
    /**
226
     * @inheritdoc
227
     */
228
    public function getTimestamps()
229
    {
230
        return $this->timestamps;
231
    }
232
233
    /**
234
     * Return the jsonLD version of a calendar.
235
     */
236
    public function toJsonLd()
237
    {
238
        $jsonLd = [];
239
240
        $jsonLd['calendarType'] = $this->getType()->toNative();
241
        // All calendar types allow startDate (and endDate).
242
        // One timestamp - full day.
243
        // One timestamp - start hour.
244
        // One timestamp - start and end hour.
245
        empty($this->startDate) ?: $jsonLd['startDate'] = $this->getStartDate()->format(DateTime::ATOM);
246
        empty($this->endDate) ?: $jsonLd['endDate'] = $this->getEndDate()->format(DateTime::ATOM);
247
248
249
        $timestamps = $this->getTimestamps();
250
        if (!empty($timestamps)) {
251
            $jsonLd['subEvent'] = array();
252
            foreach ($timestamps as $timestamp) {
253
                $jsonLd['subEvent'][] = array(
254
                  '@type' => 'Event',
255
                  'startDate' => $timestamp->getStartDate()->format(DateTime::ATOM),
256
                  'endDate' => $timestamp->getEndDate()->format(DateTime::ATOM),
257
                );
258
            }
259
        }
260
261
        // Period.
262
        // Period with openingtimes.
263
        // Permanent - "altijd open".
264
        // Permanent - with openingtimes
265
        $openingHours = $this->getOpeningHours();
266
        if (!empty($openingHours)) {
267
            $jsonLd['openingHours'] = array();
268
            foreach ($openingHours as $openingHour) {
269
                $jsonLd['openingHours'][] = $openingHour->serialize();
270
            }
271
        }
272
273
        return $jsonLd;
274
    }
275
276
    /**
277
     * @param Calendar $otherCalendar
278
     * @return bool
279
     */
280
    public function sameAs(Calendar $otherCalendar)
281
    {
282
        return $this->toJsonLd() == $otherCalendar->toJsonLd();
283
    }
284
285
    /**
286
     * @param Udb3ModelCalendar $calendar
287
     * @return self
288
     */
289
    public static function fromUdb3ModelCalendar(Udb3ModelCalendar $calendar)
290
    {
291
        $type = CalendarType::fromNative($calendar->getType()->toString());
292
293
        $startDate = null;
294
        $endDate = null;
295
        $timestamps = [];
296
        $openingHours = [];
297
298
        if ($calendar instanceof CalendarWithDateRange) {
299
            $startDate = $calendar->getStartDate();
300
            $endDate = $calendar->getEndDate();
301
        }
302
303
        if ($calendar instanceof CalendarWithSubEvents) {
304
            $timestamps = array_map(
305
                function (DateRange $dateRange) {
306
                    return Timestamp::fromUdb3ModelDateRange($dateRange);
307
                },
308
                $calendar->getSubEvents()->toArray()
309
            );
310
        }
311
312
        if ($calendar instanceof CalendarWithOpeningHours) {
313
            $openingHours = array_map(
314
                function (Udb3ModelOpeningHour $openingHour) {
315
                    return OpeningHour::fromUdb3ModelOpeningHour($openingHour);
316
                },
317
                $calendar->getOpeningHours()->toArray()
318
            );
319
        }
320
321
        return new self($type, $startDate, $endDate, $timestamps, $openingHours);
322
    }
323
}
324