Completed
Pull Request — master (#345)
by
unknown
05:39
created

Calendar   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 280
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Importance

Changes 0
Metric Value
wmc 41
lcom 2
cbo 8
dl 0
loc 280
rs 8.2769
c 0
b 0
f 0

12 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
B fromUdb3ModelCalendar() 0 34 4

How to fix   Complexity   

Complex Class

Complex classes like Calendar often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Calendar, and based on these observations, apply Extract Interface, too.

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
        return $this->startDate;
185
    }
186
187
    /**
188
     * @inheritdoc
189
     */
190
    public function getEndDate()
191
    {
192
        return $this->endDate;
193
    }
194
195
    /**
196
     * @inheritdoc
197
     */
198
    public function getOpeningHours()
199
    {
200
        return $this->openingHours;
201
    }
202
203
    /**
204
     * @inheritdoc
205
     */
206
    public function getTimestamps()
207
    {
208
        return $this->timestamps;
209
    }
210
211
    /**
212
     * Return the jsonLD version of a calendar.
213
     */
214
    public function toJsonLd()
215
    {
216
        $jsonLd = [];
217
218
        $jsonLd['calendarType'] = $this->getType()->toNative();
219
        // All calendar types allow startDate (and endDate).
220
        // One timestamp - full day.
221
        // One timestamp - start hour.
222
        // One timestamp - start and end hour.
223
        empty($this->startDate) ?: $jsonLd['startDate'] = $this->getStartDate()->format(DateTime::ATOM);
224
        empty($this->endDate) ?: $jsonLd['endDate'] = $this->getEndDate()->format(DateTime::ATOM);
225
226
227
        $timestamps = $this->getTimestamps();
228
        if (!empty($timestamps)) {
229
            $jsonLd['subEvent'] = array();
230
            foreach ($timestamps as $timestamp) {
231
                $jsonLd['subEvent'][] = array(
232
                  '@type' => 'Event',
233
                  'startDate' => $timestamp->getStartDate()->format(DateTime::ATOM),
234
                  'endDate' => $timestamp->getEndDate()->format(DateTime::ATOM),
235
                );
236
            }
237
        }
238
239
        // Period.
240
        // Period with openingtimes.
241
        // Permanent - "altijd open".
242
        // Permanent - with openingtimes
243
        $openingHours = $this->getOpeningHours();
244
        if (!empty($openingHours)) {
245
            $jsonLd['openingHours'] = array();
246
            foreach ($openingHours as $openingHour) {
247
                $jsonLd['openingHours'][] = $openingHour->serialize();
248
            }
249
        }
250
251
        return $jsonLd;
252
    }
253
    
254
    /**
255
     * @param Calendar $otherCalendar
256
     * @return bool
257
     */
258
    public function sameAs(Calendar $otherCalendar)
259
    {
260
        return $this->toJsonLd() == $otherCalendar->toJsonLd();
261
    }
262
263
    /**
264
     * @param Udb3ModelCalendar $calendar
265
     * @return self
266
     */
267
    public static function fromUdb3ModelCalendar(Udb3ModelCalendar $calendar)
268
    {
269
        $type = CalendarType::fromNative($calendar->getType()->toString());
270
271
        $startDate = null;
272
        $endDate = null;
273
        $timestamps = [];
274
        $openingHours = [];
275
276
        if ($calendar instanceof CalendarWithDateRange) {
277
            $startDate = $calendar->getStartDate();
278
            $endDate = $calendar->getEndDate();
279
        }
280
281
        if ($calendar instanceof CalendarWithSubEvents) {
282
            $timestamps = array_map(
283
                function (DateRange $dateRange) {
284
                    return Timestamp::fromUdb3ModelDateRange($dateRange);
285
                },
286
                $calendar->getSubEvents()->toArray()
287
            );
288
        }
289
290
        if ($calendar instanceof CalendarWithOpeningHours) {
291
            $openingHours = array_map(
292
                function (Udb3ModelOpeningHour $openingHour) {
293
                    return OpeningHour::fromUdb3ModelOpeningHour($openingHour);
294
                },
295
                $calendar->getOpeningHours()->toArray()
296
            );
297
        }
298
299
        return new self($type, $startDate, $endDate, $timestamps, $openingHours);
300
    }
301
}
302