Completed
Pull Request — master (#234)
by Kristof
05:00
created

Calendar::__construct()   D

Complexity

Conditions 9
Paths 4

Size

Total Lines 25
Code Lines 17

Duplication

Lines 6
Ratio 24 %

Importance

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