Completed
Pull Request — master (#342)
by Luc
05:03
created

Calendar   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 241
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 3
dl 0
loc 241
rs 8.6
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 33 11
A getType() 0 4 1
B serialize() 0 27 5
B deserialize() 0 20 5
A deserializeDateTime() 0 14 3
A getStartDate() 0 4 1
A getEndDate() 0 4 1
A getOpeningHours() 0 4 1
A getTimestamps() 0 4 1
C toJsonLd() 0 39 7
A sameAs() 0 4 1
1
<?php
2
3
namespace CultuurNet\UDB3;
4
5
use Broadway\Serializer\SerializableInterface;
6
use CultuurNet\UDB3\Calendar\OpeningHour;
7
use DateTime;
8
use DateTimeInterface;
9
use DateTimeZone;
10
use InvalidArgumentException;
11
12
/**
13
 * Calendar for events and places.
14
 */
15
class Calendar implements CalendarInterface, JsonLdSerializableInterface, SerializableInterface
16
{
17
18
    /**
19
     * @var CalendarType
20
     */
21
    protected $type = null;
22
23
    /**
24
     * @var DateTimeInterface
25
     */
26
    protected $startDate = null;
27
28
    /**
29
     * @var DateTimeInterface
30
     */
31
    protected $endDate = null;
32
33
    /**
34
     * @var Timestamp[]
35
     */
36
    protected $timestamps = array();
37
38
    /**
39
     * @var OpeningHour[]
40
     */
41
    protected $openingHours = array();
42
43
    /**
44
     * @param CalendarType $type
45
     * @param DateTimeInterface|null $startDate
46
     * @param DateTimeInterface|null $endDate
47
     * @param Timestamp[] $timestamps
48
     * @param OpeningHour[] $openingHours
49
     */
50
    public function __construct(
51
        CalendarType $type,
52
        DateTimeInterface $startDate = null,
53
        DateTimeInterface $endDate = null,
54
        array $timestamps = array(),
55
        array $openingHours = array()
56
    ) {
57
        if (($type->is(CalendarType::MULTIPLE()) || $type->is(CalendarType::SINGLE())) && empty($startDate)) {
58
            throw new \UnexpectedValueException('Start date can not be empty for calendar type: ' . $type . '.');
59
        }
60
61
        if ($type->is(CalendarType::PERIODIC()) && (empty($startDate) || empty($endDate))) {
62
            throw new \UnexpectedValueException('A period should have a start- and end-date.');
63
        }
64
65
        foreach ($timestamps as $timestamp) {
66
            if (!is_a($timestamp, Timestamp::class)) {
67
                throw new \InvalidArgumentException('Timestamps should have type TimeStamp.');
68
            }
69
        }
70
71
        foreach ($openingHours as $openingHour) {
72
            if (!is_a($openingHour, OpeningHour::class)) {
73
                throw new \InvalidArgumentException('OpeningHours should have type OpeningHour.');
74
            }
75
        }
76
77
        $this->type = $type->toNative();
78
        $this->startDate = $startDate;
79
        $this->endDate = $endDate;
80
        $this->timestamps = $timestamps;
81
        $this->openingHours = $openingHours;
82
    }
83
84
    /**
85
     * @inheritdoc
86
     */
87
    public function getType()
88
    {
89
        return CalendarType::fromNative($this->type);
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95
    public function serialize()
96
    {
97
        $serializedTimestamps = array_map(
98
            function (Timestamp $timestamp) {
99
                return $timestamp->serialize();
100
            },
101
            $this->timestamps
102
        );
103
104
        $serializedOpeningHours = array_map(
105
            function (OpeningHour $openingHour) {
106
                return $openingHour->serialize();
107
            },
108
            $this->openingHours
109
        );
110
111
        $calendar = [
112
          'type' => $this->type,
113
        ];
114
115
        empty($this->startDate) ?: $calendar['startDate'] = $this->startDate->format(DateTime::ATOM);
116
        empty($this->endDate) ?: $calendar['endDate'] = $this->endDate->format(DateTime::ATOM);
117
        empty($serializedTimestamps) ?: $calendar['timestamps'] = $serializedTimestamps;
118
        empty($serializedOpeningHours) ?: $calendar['openingHours'] = $serializedOpeningHours;
119
120
        return $calendar;
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126
    public static function deserialize(array $data)
127
    {
128
        return new static(
129
            CalendarType::fromNative($data['type']),
130
            !empty($data['startDate']) ? self::deserializeDateTime($data['startDate']) : null,
131
            !empty($data['endDate']) ? self::deserializeDateTime($data['endDate']) : null,
132
            !empty($data['timestamps']) ? array_map(
133
                function ($timestamp) {
134
                    return Timestamp::deserialize($timestamp);
135
                },
136
                $data['timestamps']
137
            ) : [],
138
            !empty($data['openingHours']) ? array_map(
139
                function ($openingHour) {
140
                    return OpeningHour::deserialize($openingHour);
141
                },
142
                $data['openingHours']
143
            ) : []
144
        );
145
    }
146
147
    /**
148
     * This deserialization function takes into account old data that might be missing a timezone.
149
     * It will fall back to creating a DateTime object and assume Brussels.
150
     * If this still fails an error will be thrown.
151
     *
152
     * @param $dateTimeData
153
     * @return DateTime
154
     *
155
     * @throws InvalidArgumentException
156
     */
157
    private static function deserializeDateTime($dateTimeData)
158
    {
159
        $dateTime = DateTime::createFromFormat(DateTime::ATOM, $dateTimeData);
160
161
        if ($dateTime === false) {
162
            $dateTime = DateTime::createFromFormat('Y-m-d\TH:i:s', $dateTimeData, new DateTimeZone('Europe/Brussels'));
163
164
            if (!$dateTime) {
165
                throw new InvalidArgumentException('Invalid date string provided for timestamp, ISO8601 expected!');
166
            }
167
        }
168
169
        return $dateTime;
170
    }
171
172
    /**
173
     * @inheritdoc
174
     */
175
    public function getStartDate()
176
    {
177
        return $this->startDate;
178
    }
179
180
    /**
181
     * @inheritdoc
182
     */
183
    public function getEndDate()
184
    {
185
        return $this->endDate;
186
    }
187
188
    /**
189
     * @inheritdoc
190
     */
191
    public function getOpeningHours()
192
    {
193
        return $this->openingHours;
194
    }
195
196
    /**
197
     * @inheritdoc
198
     */
199
    public function getTimestamps()
200
    {
201
        return $this->timestamps;
202
    }
203
204
    /**
205
     * Return the jsonLD version of a calendar.
206
     */
207
    public function toJsonLd()
208
    {
209
        $jsonLd = [];
210
211
        $jsonLd['calendarType'] = $this->getType()->toNative();
212
        // All calendar types allow startDate (and endDate).
213
        // One timestamp - full day.
214
        // One timestamp - start hour.
215
        // One timestamp - start and end hour.
216
        empty($this->startDate) ?: $jsonLd['startDate'] = $this->getStartDate()->format(DateTime::ATOM);
217
        empty($this->endDate) ?: $jsonLd['endDate'] = $this->getEndDate()->format(DateTime::ATOM);
218
219
220
        $timestamps = $this->getTimestamps();
221
        if (!empty($timestamps)) {
222
            $jsonLd['subEvent'] = array();
223
            foreach ($timestamps as $timestamp) {
224
                $jsonLd['subEvent'][] = array(
225
                  '@type' => 'Event',
226
                  'startDate' => $timestamp->getStartDate()->format(DateTime::ATOM),
227
                  'endDate' => $timestamp->getEndDate()->format(DateTime::ATOM),
228
                );
229
            }
230
        }
231
232
        // Period.
233
        // Period with openingtimes.
234
        // Permanent - "altijd open".
235
        // Permanent - with openingtimes
236
        $openingHours = $this->getOpeningHours();
237
        if (!empty($openingHours)) {
238
            $jsonLd['openingHours'] = array();
239
            foreach ($openingHours as $openingHour) {
240
                $jsonLd['openingHours'][] = $openingHour->serialize();
241
            }
242
        }
243
244
        return $jsonLd;
245
    }
246
247
    /**
248
     * @param Calendar $otherCalendar
249
     * @return bool
250
     */
251
    public function sameAs(Calendar $otherCalendar)
252
    {
253
        return $this->toJsonLd() == $otherCalendar->toJsonLd();
254
    }
255
}
256