1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace CultuurNet\UDB3\Organizer; |
4
|
|
|
|
5
|
|
|
use Broadway\EventSourcing\EventSourcedAggregateRoot; |
6
|
|
|
use CultuurNet\UDB3\Address\Address; |
7
|
|
|
use CultuurNet\UDB3\Cdb\ActorItemFactory; |
8
|
|
|
use CultuurNet\UDB3\Cdb\UpdateableWithCdbXmlInterface; |
9
|
|
|
use CultuurNet\UDB3\ContactPoint; |
10
|
|
|
use CultuurNet\UDB3\Label; |
11
|
|
|
use CultuurNet\UDB3\LabelAwareAggregateRoot; |
12
|
|
|
use CultuurNet\UDB3\LabelCollection; |
13
|
|
|
use CultuurNet\UDB3\Language; |
14
|
|
|
use CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\LabelName; |
15
|
|
|
use CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\Labels; |
16
|
|
|
use CultuurNet\UDB3\Organizer\Events\AddressTranslated; |
17
|
|
|
use CultuurNet\UDB3\Organizer\Events\AddressUpdated; |
18
|
|
|
use CultuurNet\UDB3\Organizer\Events\ContactPointUpdated; |
19
|
|
|
use CultuurNet\UDB3\Organizer\Events\LabelAdded; |
20
|
|
|
use CultuurNet\UDB3\Organizer\Events\LabelRemoved; |
21
|
|
|
use CultuurNet\UDB3\Organizer\Events\LabelsImported; |
22
|
|
|
use CultuurNet\UDB3\Organizer\Events\OrganizerCreated; |
23
|
|
|
use CultuurNet\UDB3\Organizer\Events\OrganizerCreatedWithUniqueWebsite; |
24
|
|
|
use CultuurNet\UDB3\Organizer\Events\OrganizerDeleted; |
25
|
|
|
use CultuurNet\UDB3\Organizer\Events\OrganizerImportedFromUDB2; |
26
|
|
|
use CultuurNet\UDB3\Organizer\Events\OrganizerUpdatedFromUDB2; |
27
|
|
|
use CultuurNet\UDB3\Organizer\Events\TitleTranslated; |
28
|
|
|
use CultuurNet\UDB3\Organizer\Events\TitleUpdated; |
29
|
|
|
use CultuurNet\UDB3\Organizer\Events\WebsiteUpdated; |
30
|
|
|
use CultuurNet\UDB3\Title; |
31
|
|
|
use ValueObjects\Web\Url; |
32
|
|
|
|
33
|
|
|
class Organizer extends EventSourcedAggregateRoot implements UpdateableWithCdbXmlInterface, LabelAwareAggregateRoot |
34
|
|
|
{ |
35
|
|
|
/** |
36
|
|
|
* The actor id. |
37
|
|
|
* |
38
|
|
|
* @var string |
39
|
|
|
*/ |
40
|
|
|
protected $actorId; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var Language |
44
|
|
|
*/ |
45
|
|
|
private $mainLanguage; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var Url |
49
|
|
|
*/ |
50
|
|
|
private $website; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var Title[] |
54
|
|
|
*/ |
55
|
|
|
private $titles; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var Address[]|null |
59
|
|
|
*/ |
60
|
|
|
private $addresses; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @var ContactPoint |
64
|
|
|
*/ |
65
|
|
|
private $contactPoint; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @var LabelCollection|Label[] |
69
|
|
|
*/ |
70
|
|
|
private $labels; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* {@inheritdoc} |
74
|
|
|
*/ |
75
|
|
|
public function getAggregateRootId() |
76
|
|
|
{ |
77
|
|
|
return $this->actorId; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
public function __construct() |
81
|
|
|
{ |
82
|
|
|
// Contact points can be empty, but we only want to start recording |
83
|
|
|
// ContactPointUpdated events as soon as the organizer is updated |
84
|
|
|
// with a non-empty contact point. To enforce this we initialize the |
85
|
|
|
// aggregate state with an empty contact point. |
86
|
|
|
$this->contactPoint = new ContactPoint(); |
87
|
|
|
$this->labels = new LabelCollection(); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Import from UDB2. |
92
|
|
|
* |
93
|
|
|
* @param string $actorId |
94
|
|
|
* The actor id. |
95
|
|
|
* @param string $cdbXml |
96
|
|
|
* The cdb xml. |
97
|
|
|
* @param string $cdbXmlNamespaceUri |
98
|
|
|
* The cdb xml namespace uri. |
99
|
|
|
* |
100
|
|
|
* @return Organizer |
101
|
|
|
* The actor. |
102
|
|
|
*/ |
103
|
|
|
public static function importFromUDB2( |
104
|
|
|
$actorId, |
105
|
|
|
$cdbXml, |
106
|
|
|
$cdbXmlNamespaceUri |
107
|
|
|
) { |
108
|
|
|
$organizer = new static(); |
109
|
|
|
$organizer->apply( |
110
|
|
|
new OrganizerImportedFromUDB2( |
111
|
|
|
$actorId, |
112
|
|
|
$cdbXml, |
113
|
|
|
$cdbXmlNamespaceUri |
114
|
|
|
) |
115
|
|
|
); |
116
|
|
|
|
117
|
|
|
return $organizer; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Factory method to create a new Organizer. |
122
|
|
|
* |
123
|
|
|
* @param string $id |
124
|
|
|
* @param Language $mainLanguage |
125
|
|
|
* @param Url $website |
126
|
|
|
* @param Title $title |
127
|
|
|
* @return Organizer |
128
|
|
|
*/ |
129
|
|
|
public static function create( |
130
|
|
|
$id, |
131
|
|
|
Language $mainLanguage, |
132
|
|
|
Url $website, |
133
|
|
|
Title $title |
134
|
|
|
) { |
135
|
|
|
$organizer = new self(); |
136
|
|
|
|
137
|
|
|
$organizer->apply( |
138
|
|
|
new OrganizerCreatedWithUniqueWebsite($id, $mainLanguage, $website, $title) |
139
|
|
|
); |
140
|
|
|
|
141
|
|
|
return $organizer; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* @inheritdoc |
146
|
|
|
*/ |
147
|
|
|
public function updateWithCdbXml($cdbXml, $cdbXmlNamespaceUri) |
148
|
|
|
{ |
149
|
|
|
$this->apply( |
150
|
|
|
new OrganizerUpdatedFromUDB2( |
151
|
|
|
$this->actorId, |
152
|
|
|
$cdbXml, |
153
|
|
|
$cdbXmlNamespaceUri |
154
|
|
|
) |
155
|
|
|
); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* @param Url $website |
160
|
|
|
*/ |
161
|
|
|
public function updateWebsite(Url $website) |
162
|
|
|
{ |
163
|
|
|
if (is_null($this->website) || !$this->website->sameValueAs($website)) { |
164
|
|
|
$this->apply( |
165
|
|
|
new WebsiteUpdated( |
166
|
|
|
$this->actorId, |
167
|
|
|
$website |
168
|
|
|
) |
169
|
|
|
); |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* @param Title $title |
175
|
|
|
* @param Language $language |
176
|
|
|
*/ |
177
|
|
|
public function updateTitle( |
178
|
|
|
Title $title, |
179
|
|
|
Language $language |
180
|
|
|
) { |
181
|
|
|
if ($this->isTitleChanged($title, $language)) { |
182
|
|
|
if ($language->getCode() !== $this->mainLanguage->getCode()) { |
183
|
|
|
$event = new TitleTranslated( |
184
|
|
|
$this->actorId, |
185
|
|
|
$title, |
186
|
|
|
$language |
187
|
|
|
); |
188
|
|
|
} else { |
189
|
|
|
$event = new TitleUpdated( |
190
|
|
|
$this->actorId, |
191
|
|
|
$title |
192
|
|
|
); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$this->apply($event); |
196
|
|
|
} |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* @param Address $address |
201
|
|
|
* @param Language $language |
202
|
|
|
*/ |
203
|
|
View Code Duplication |
public function updateAddress( |
|
|
|
|
204
|
|
|
Address $address, |
205
|
|
|
Language $language |
206
|
|
|
) { |
207
|
|
|
if ($this->isAddressChanged($address, $language)) { |
208
|
|
|
if ($language->getCode() !== $this->mainLanguage->getCode()) { |
209
|
|
|
$event = new AddressTranslated( |
210
|
|
|
$this->actorId, |
211
|
|
|
$address, |
212
|
|
|
$language |
213
|
|
|
); |
214
|
|
|
} else { |
215
|
|
|
$event = new AddressUpdated( |
216
|
|
|
$this->actorId, |
217
|
|
|
$address |
218
|
|
|
); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
$this->apply($event); |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* @param ContactPoint $contactPoint |
227
|
|
|
*/ |
228
|
|
|
public function updateContactPoint(ContactPoint $contactPoint) |
229
|
|
|
{ |
230
|
|
|
if (!$this->contactPoint->sameAs($contactPoint)) { |
231
|
|
|
$this->apply( |
232
|
|
|
new ContactPointUpdated($this->actorId, $contactPoint) |
233
|
|
|
); |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* @inheritdoc |
239
|
|
|
*/ |
240
|
|
|
public function addLabel(Label $label) |
241
|
|
|
{ |
242
|
|
|
if (!$this->labels->contains($label)) { |
243
|
|
|
$this->apply(new LabelAdded($this->actorId, $label)); |
244
|
|
|
} |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* @inheritdoc |
249
|
|
|
*/ |
250
|
|
|
public function removeLabel(Label $label) |
251
|
|
|
{ |
252
|
|
|
if ($this->labels->contains($label)) { |
253
|
|
|
$this->apply(new LabelRemoved($this->actorId, $label)); |
254
|
|
|
} |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* @param Labels $labels |
259
|
|
|
*/ |
260
|
|
|
public function importLabels(Labels $labels) |
261
|
|
|
{ |
262
|
|
|
// Convert the imported labels to label collection. |
263
|
|
|
$importLabelsCollection = new LabelCollection( |
264
|
|
|
array_map( |
265
|
|
|
function (\CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\Label $label) { |
266
|
|
|
return new Label( |
267
|
|
|
$label->getName()->toString(), |
268
|
|
|
$label->isVisible() |
269
|
|
|
); |
270
|
|
|
}, |
271
|
|
|
$labels->toArray() |
272
|
|
|
) |
273
|
|
|
); |
274
|
|
|
|
275
|
|
|
// What are the added labels? |
276
|
|
|
// Labels which are not inside the internal state but inside the imported labels |
277
|
|
|
$addedLabels = new LabelCollection(); |
278
|
|
|
foreach ($importLabelsCollection->asArray() as $label) { |
279
|
|
|
if (!$this->labels->contains($label)) { |
280
|
|
|
$addedLabels = $addedLabels->with($label); |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
// Fire a LabelsImported for all new labels. |
285
|
|
|
$importLabels = new Labels(); |
286
|
|
View Code Duplication |
foreach ($addedLabels->asArray() as $addedLabel) { |
|
|
|
|
287
|
|
|
$importLabels = $importLabels->with( |
288
|
|
|
new \CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\Label( |
289
|
|
|
new LabelName((string) $addedLabel), |
290
|
|
|
$addedLabel->isVisible() |
291
|
|
|
) |
292
|
|
|
); |
293
|
|
|
} |
294
|
|
|
if ($importLabels->count() > 0) { |
295
|
|
|
$this->apply(new LabelsImported( |
296
|
|
|
$this->actorId, |
297
|
|
|
$importLabels |
298
|
|
|
)); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
// For each added label fire a LabelAdded event. |
302
|
|
|
foreach ($addedLabels->asArray() as $label) { |
303
|
|
|
$this->apply(new LabelAdded($this->actorId, $label)); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
// What are the deleted labels? |
307
|
|
|
// Labels which are inside the internal state but not inside imported labels. |
308
|
|
|
// For each deleted label fire a LabelDeleted event. |
309
|
|
|
foreach ($this->labels->asArray() as $label) { |
310
|
|
|
if (!$importLabelsCollection->contains($label)) { |
311
|
|
|
$this->apply(new LabelRemoved($this->actorId, $label)); |
312
|
|
|
} |
313
|
|
|
} |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
public function delete() |
317
|
|
|
{ |
318
|
|
|
$this->apply( |
319
|
|
|
new OrganizerDeleted($this->getAggregateRootId()) |
320
|
|
|
); |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* Apply the organizer created event. |
325
|
|
|
* @param OrganizerCreated $organizerCreated |
326
|
|
|
*/ |
327
|
|
|
protected function applyOrganizerCreated(OrganizerCreated $organizerCreated) |
328
|
|
|
{ |
329
|
|
|
$this->actorId = $organizerCreated->getOrganizerId(); |
330
|
|
|
|
331
|
|
|
$this->mainLanguage = new Language('nl'); |
332
|
|
|
|
333
|
|
|
$this->setTitle($organizerCreated->getTitle(), $this->mainLanguage); |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Apply the organizer created event. |
338
|
|
|
* @param OrganizerCreatedWithUniqueWebsite $organizerCreated |
339
|
|
|
*/ |
340
|
|
|
protected function applyOrganizerCreatedWithUniqueWebsite(OrganizerCreatedWithUniqueWebsite $organizerCreated) |
341
|
|
|
{ |
342
|
|
|
$this->actorId = $organizerCreated->getOrganizerId(); |
343
|
|
|
|
344
|
|
|
$this->mainLanguage = $organizerCreated->getMainLanguage(); |
345
|
|
|
|
346
|
|
|
$this->website = $organizerCreated->getWebsite(); |
347
|
|
|
|
348
|
|
|
$this->setTitle($organizerCreated->getTitle(), $this->mainLanguage); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* @param OrganizerImportedFromUDB2 $organizerImported |
353
|
|
|
* @throws \CultureFeed_Cdb_ParseException |
354
|
|
|
*/ |
355
|
|
|
protected function applyOrganizerImportedFromUDB2( |
356
|
|
|
OrganizerImportedFromUDB2 $organizerImported |
357
|
|
|
) { |
358
|
|
|
$this->actorId = (string) $organizerImported->getActorId(); |
359
|
|
|
|
360
|
|
|
// On import from UDB2 the default main language is 'nl'. |
361
|
|
|
$this->mainLanguage = new Language('nl'); |
362
|
|
|
|
363
|
|
|
$actor = ActorItemFactory::createActorFromCdbXml( |
364
|
|
|
$organizerImported->getCdbXmlNamespaceUri(), |
365
|
|
|
$organizerImported->getCdbXml() |
366
|
|
|
); |
367
|
|
|
|
368
|
|
|
$this->setTitle($this->getTitle($actor), $this->mainLanguage); |
|
|
|
|
369
|
|
|
|
370
|
|
|
$this->labels = LabelCollection::fromKeywords($actor->getKeywords(true)); |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* @param OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2 |
375
|
|
|
* @throws \CultureFeed_Cdb_ParseException |
376
|
|
|
*/ |
377
|
|
|
protected function applyOrganizerUpdatedFromUDB2( |
378
|
|
|
OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2 |
379
|
|
|
) { |
380
|
|
|
// Note: never change the main language on update from UDB2. |
381
|
|
|
|
382
|
|
|
$actor = ActorItemFactory::createActorFromCdbXml( |
383
|
|
|
$organizerUpdatedFromUDB2->getCdbXmlNamespaceUri(), |
384
|
|
|
$organizerUpdatedFromUDB2->getCdbXml() |
385
|
|
|
); |
386
|
|
|
|
387
|
|
|
$this->setTitle($this->getTitle($actor), $this->mainLanguage); |
|
|
|
|
388
|
|
|
|
389
|
|
|
$this->labels = LabelCollection::fromKeywords($actor->getKeywords(true)); |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* @param WebsiteUpdated $websiteUpdated |
394
|
|
|
*/ |
395
|
|
|
protected function applyWebsiteUpdated(WebsiteUpdated $websiteUpdated) |
396
|
|
|
{ |
397
|
|
|
$this->website = $websiteUpdated->getWebsite(); |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
/** |
401
|
|
|
* @param TitleUpdated $titleUpdated |
402
|
|
|
*/ |
403
|
|
|
protected function applyTitleUpdated(TitleUpdated $titleUpdated) |
404
|
|
|
{ |
405
|
|
|
$this->setTitle($titleUpdated->getTitle(), $this->mainLanguage); |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
/** |
409
|
|
|
* @param TitleTranslated $titleTranslated |
410
|
|
|
*/ |
411
|
|
|
protected function applyTitleTranslated(TitleTranslated $titleTranslated) |
412
|
|
|
{ |
413
|
|
|
$this->setTitle($titleTranslated->getTitle(), $titleTranslated->getLanguage()); |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* @param AddressUpdated $addressUpdated |
418
|
|
|
*/ |
419
|
|
|
protected function applyAddressUpdated(AddressUpdated $addressUpdated) |
420
|
|
|
{ |
421
|
|
|
$this->setAddress($addressUpdated->getAddress(), $this->mainLanguage); |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* @param AddressTranslated $addressTranslated |
426
|
|
|
*/ |
427
|
|
|
protected function applyAddressTranslated(AddressTranslated $addressTranslated) |
428
|
|
|
{ |
429
|
|
|
$this->setAddress($addressTranslated->getAddress(), $addressTranslated->getLanguage()); |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
/** |
433
|
|
|
* @param ContactPointUpdated $contactPointUpdated |
434
|
|
|
*/ |
435
|
|
|
protected function applyContactPointUpdated(ContactPointUpdated $contactPointUpdated) |
436
|
|
|
{ |
437
|
|
|
$this->contactPoint = $contactPointUpdated->getContactPoint(); |
438
|
|
|
} |
439
|
|
|
|
440
|
|
|
/** |
441
|
|
|
* @param LabelAdded $labelAdded |
442
|
|
|
*/ |
443
|
|
|
protected function applyLabelAdded(LabelAdded $labelAdded) |
444
|
|
|
{ |
445
|
|
|
$this->labels = $this->labels->with($labelAdded->getLabel()); |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
/** |
449
|
|
|
* @param LabelRemoved $labelRemoved |
450
|
|
|
*/ |
451
|
|
|
protected function applyLabelRemoved(LabelRemoved $labelRemoved) |
452
|
|
|
{ |
453
|
|
|
$this->labels = $this->labels->without($labelRemoved->getLabel()); |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
/** |
457
|
|
|
* @param \CultureFeed_Cdb_Item_Actor $actor |
458
|
|
|
* @return null|Title |
459
|
|
|
*/ |
460
|
|
|
private function getTitle(\CultureFeed_Cdb_Item_Actor $actor) |
461
|
|
|
{ |
462
|
|
|
$details = $actor->getDetails(); |
463
|
|
|
$details->rewind(); |
464
|
|
|
|
465
|
|
|
// The first language detail found will be used to retrieve |
466
|
|
|
// properties from which in UDB3 are not any longer considered |
467
|
|
|
// to be language specific. |
468
|
|
|
if ($details->valid()) { |
469
|
|
|
return new Title($details->current()->getTitle()); |
470
|
|
|
} else { |
471
|
|
|
return null; |
472
|
|
|
} |
473
|
|
|
} |
474
|
|
|
|
475
|
|
|
/** |
476
|
|
|
* @param Title $title |
477
|
|
|
* @param Language $language |
478
|
|
|
*/ |
479
|
|
|
private function setTitle(Title $title, Language $language) |
480
|
|
|
{ |
481
|
|
|
$this->titles[$language->getCode()] = $title; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* @param Title $title |
486
|
|
|
* @param Language $language |
487
|
|
|
* @return bool |
488
|
|
|
*/ |
489
|
|
|
private function isTitleChanged(Title $title, Language $language) |
490
|
|
|
{ |
491
|
|
|
return !isset($this->titles[$language->getCode()]) || |
492
|
|
|
!$title->sameValueAs($this->titles[$language->getCode()]); |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* @param Address $address |
497
|
|
|
* @param Language $language |
498
|
|
|
*/ |
499
|
|
|
private function setAddress(Address $address, Language $language) |
500
|
|
|
{ |
501
|
|
|
$this->addresses[$language->getCode()] = $address; |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
/** |
505
|
|
|
* @param Address $address |
506
|
|
|
* @param Language $language |
507
|
|
|
* @return bool |
508
|
|
|
*/ |
509
|
|
|
private function isAddressChanged(Address $address, Language $language) |
510
|
|
|
{ |
511
|
|
|
return !isset($this->addresses[$language->getCode()]) || |
512
|
|
|
!$address->sameAs($this->addresses[$language->getCode()]); |
513
|
|
|
} |
514
|
|
|
} |
515
|
|
|
|
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.