Completed
Push — master ( cbe6f3...9d07ea )
by Henri
02:52
created

Concept::getDate()   D

Complexity

Conditions 9
Paths 128

Size

Total Lines 43
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 43
rs 4.6666
cc 9
eloc 26
nc 128
nop 0
1
<?php
2
/**
3
 * Copyright (c) 2012-2013 Aalto University and University of Helsinki
4
 * MIT License
5
 * see LICENSE.txt for more information
6
 */
7
8
/**
9
 * Dataobject for a single concept.
10
 */
11
12
class Concept extends VocabularyDataObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
13
{
14
    /**
15
     * Stores a label string if the concept has been found through
16
     * a altLabel/label in a another language than the ui.
17
     */
18
    private $foundby;
19
    /** Type of foundby match: 'alt', 'hidden' or 'lang' */
20
    private $foundbytype;
21
    /** the EasyRdf_Graph object of the concept */
22
    private $graph;
23
    private $clang;
24
25
    /** concept properties that should not be shown to users */
26
    private $DELETED_PROPERTIES = array(
27
        'skosext:broaderGeneric', # these are remnants of bad modeling
28
        'skosext:broaderPartitive', #
29
30
        'skos:hiddenLabel', # because it's supposed to be hidden
31
        'skos:prefLabel', # handled separately by getLabel
32
        'rdfs:label', # handled separately by getLabel
33
34
        'skos:topConceptOf', # because it's too technical, not relevant for users
35
        'skos:inScheme', # should be evident in any case
36
        'skos:member', # this is shouldn't be shown on the group page
37
        'dc:created', # handled separately
38
        'dc:modified', # handled separately
39
    );
40
41
    /** related concepts that should be shown to users in the appendix */
42
    private $MAPPING_PROPERTIES = array(
43
        'skos:exactMatch',
44
        'skos:narrowMatch',
45
        'skos:broadMatch',
46
        'skos:closeMatch',
47
        'skos:relatedMatch',
48
        'rdfs:seeAlso',
49
        'owl:sameAs',
50
    );
51
52
    /**
53
     * Initializing the concept object requires the following parameters.
54
     * @param Model $model
55
     * @param Vocabulary $vocab
56
     * @param EasyRdf_Resource $resource
57
     * @param EasyRdf_Graph $graph
58
     */
59
    public function __construct($model, $vocab, $resource, $graph, $clang)
60
    {
61
        parent::__construct($model, $vocab, $resource);
62
        $this->order = array("rdf:type", "dc:isReplacedBy", "skos:definition", "skos:broader", "skos:narrower", "skos:related", "skos:altLabel", "skosmos:memberOf", "skos:note", "skos:scopeNote", "skos:historyNote", "rdfs:comment", "dc11:source", "dc:source", "skos:prefLabel");
63
        $this->graph = $graph;
64
        $this->clang = $clang;
65
        // setting the Punic plugins locale for localized datetime conversions
66
        if ($this->clang && $this->clang !== '') {
67
            Punic\Data::setDefaultLocale($clang);
68
        }
69
70
    }
71
72
    /**
73
     * Returns the concept uri.
74
     * @return string
75
     */
76
    public function getUri()
77
    {
78
        return $this->resource->getUri();
79
    }
80
81
    public function getType()
82
    {
83
        return $this->resource->types();
84
    }
85
86
    /**
87
     * Returns a boolean value indicating if the concept has been deprecated.
88
     * @return boolean
89
     */
90
    public function getDeprecated()
91
    {
92
        foreach ($this->resource->all('rdf:type') as $type) {
93
            if (strpos($type->getUri(), 'DeprecatedConcept')) {
94
                return true;
95
            }
96
        }
97
98
        return false;
99
    }
100
101
    /**
102
     * Returns a label for the concept in the ui language or if not possible in any language.
103
     * @return string
104
     */
105
    public function getLabel()
106
    {
107
        $lang = $this->clang;
108
        // 1. label in current language
109
        if ($this->resource->label($lang) !== null) {
110
            return $this->resource->label($lang);
111
        }
112
113
        // 2. label in the vocabulary default language
114 View Code Duplication
        if ($this->resource->label($this->vocab->getConfig()->getDefaultLanguage()) !== null) {
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...
115
            return $this->resource->label($this->vocab->getConfig()->getDefaultLanguage());
116
        }
117
118
        // 3. label in any language
119
        $label = $this->resource->label();
120
        // if the label lang code is a subset of the ui lang eg. en-GB
121
        if ($label !== null && strpos($label->getLang(), $this->getEnvLang() . '-') === 0) {
122
            return $label->getValue();
123
        }
124
125
        if ($label !== null) {
126
            return $label->getValue() . " (" . $label->getLang() . ")";
127
        }
128
129
        // empty
130
        return "";
131
    }
132
133
    /**
134
     * Returns a notation for the concept or null if it has not been defined.
135
     * @return string eg. '999'
136
     */
137
    public function getNotation()
138
    {
139
        $notation = $this->resource->get('skos:notation');
140
        if ($notation !== null) {
141
            return $notation->getValue();
142
        }
143
144
        return null;
145
    }
146
147
    /**
148
     * Returns the Vocabulary object or undefined if that is not available.
149
     * @return Vocabulary
150
     */
151
    public function getVocab()
152
    {
153
        return $this->vocab;
154
    }
155
156
    /**
157
     * Returns the vocabulary shortname string or id if that is not available.
158
     * @return string
159
     */
160
    public function getShortName()
161
    {
162
        return $this->vocab ? $this->vocab->getShortName() : null;
163
    }
164
165
    /**
166
     * Returns the vocabulary shortname string or id if that is not available.
167
     * @return string
168
     */
169
    public function getVocabTitle()
170
    {
171
        return $this->vocab ? $this->vocab->getTitle() : null;
172
    }
173
174
    /**
175
     * Setter for the $clang property.
176
     * @param string $clang language code eg. 'en'
177
     */
178
    public function setContentLang($clang)
179
    {
180
        $this->clang = $clang;
181
    }
182
183
    public function getContentLang()
184
    {
185
        return $this->clang;
186
    }
187
188
    /**
189
     * Setter for the $foundby property.
190
     * @param string $label label that was matched
191
     * @param string $type type of match: 'alt', 'hidden', or 'lang'
192
     */
193
    public function setFoundBy($label, $type)
194
    {
195
        $this->foundby = $label;
196
        $this->foundbytype = $type;
197
    }
198
199
    /**
200
     * Getter for the $foundby property.
201
     * @return string
202
     */
203
    public function getFoundBy()
204
    {
205
        return $this->foundby;
206
    }
207
208
    /**
209
     * Getter for the $foundbytype property.
210
     * @return string
211
     */
212
    public function getFoundByType()
213
    {
214
        return $this->foundbytype;
215
    }
216
217
    public function getMappingProperties()
218
    {
219
        $ret = array();
220
221
        $long_uris = $this->resource->propertyUris();
222
        foreach ($long_uris as &$prop) {
223 View Code Duplication
            if (EasyRdf_Namespace::shorten($prop) !== null) {
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...
224
                // shortening property labels if possible
225
                $prop = $sprop = EasyRdf_Namespace::shorten($prop);
226
            } else {
227
                $sprop = "<$prop>";
228
            }
229
            // EasyRdf requires full URIs to be in angle brackets
230
231
            if (in_array($prop, $this->MAPPING_PROPERTIES) && !in_array($prop, $this->DELETED_PROPERTIES)) {
232
                $propres = new EasyRdf_Resource($prop, $this->graph);
233
                $proplabel = $propres->label($this->getEnvLang()) ? $propres->label($this->getEnvLang()) : $propres->label(); // current language
234
                $propobj = new ConceptProperty($prop, $proplabel);
235
                if ($propobj->getLabel() !== null) {
236
                    // only display properties for which we have a label
237
                    $ret[$prop] = $propobj;
238
                }
239
240
                // Iterating through every resource and adding these to the data object.
241
                foreach ($this->resource->allResources($sprop) as $val) {
242
                    if (isset($ret[$prop])) {
243
                        // checking if the target vocabulary can be found at the skosmos endpoint
244
                        $exuri = $val->getUri();
245
                        $exvoc = $this->model->guessVocabularyFromURI($exuri);
246
                        // if not querying the uri itself
247
                        if (!$exvoc) {
248
                            $response = null;
249
                            // if told to do so in the vocabulary configuration
250
                            if ($this->vocab->getConfig()->getExternalResourcesLoading()) {
251
                                $response = $this->model->getResourceFromUri($exuri);
252
                            }
253
254
                            if ($response) {
255
                                $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $response, $prop), $this->clang);
256
                                continue;
257
                            }
258
                        }
259
                        $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $val, $prop, $this->clang), $this->clang);
260
                    }
261
                }
262
            }
263
        }
264
265
        // sorting the properties to a order preferred in the Skosmos concept page.
266
        $ret = $this->arbitrarySort($ret);
267
268
        return $ret;
269
    }
270
271
    /**
272
     * Iterates over all the properties of the concept and returns those in an array.
273
     * @return array
274
     */
275
    public function getProperties()
276
    {
277
        $properties = array();
278
        $narrowers_by_uri = array();
279
        $in_a_collection = array();
280
        $members_array = array();
281
        $long_uris = $this->resource->propertyUris();
282
        $duplicates = array();
283
        $ret = array();
284
285
        // looking for collections and linking those with their narrower concepts
286
        if ($this->vocab->getConfig()->getArrayClassURI() !== null) {
287
            $collections = $this->graph->allOfType($this->vocab->getConfig()->getArrayClassURI());
288
            if (sizeof($collections) > 0) {
289
                // indexing the narrowers once to avoid iterating all of them with every collection
290
                foreach ($this->resource->allResources('skos:narrower') as $narrower) {
291
                    $narrowers_by_uri[$narrower->getUri()] = $narrower;
292
                }
293
294
                foreach ($collections as $coll) {
295
                    $curr_coll_members = $this->getCollectionMembers($coll, $narrowers_by_uri);
296
                    foreach ($curr_coll_members as $collection) {
297
                        if ($collection->getSubMembers()) {
298
                            $submembers = $collection->getSubMembers();
299
                            foreach ($submembers as $member) {
300
                                $in_a_collection[$member->getUri()] = true;
301
                            }
302
303
                        }
304
                    }
305
306
                    if (isset($collection) && $collection->getSubMembers()) {
307
                        $members_array = array_merge($curr_coll_members, $members_array);
308
                    }
309
310
                }
311
                $properties['skos:narrower'] = $members_array;
312
            }
313
        }
314
315
        foreach ($long_uris as &$prop) {
316 View Code Duplication
            if (EasyRdf_Namespace::shorten($prop) !== null) {
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...
317
                // shortening property labels if possible
318
                $prop = $sprop = EasyRdf_Namespace::shorten($prop);
319
            } else {
320
                $sprop = "<$prop>";
321
            }
322
            // EasyRdf requires full URIs to be in angle brackets
323
324
            if (!in_array($prop, $this->DELETED_PROPERTIES)) {
325
                $propres = new EasyRdf_Resource($prop, $this->graph);
326
                $proplabel = $propres->label($this->getEnvLang()) ? $propres->label($this->getEnvLang()) : $propres->label();
327
                $propobj = new ConceptProperty($prop, $proplabel);
328
329
                if ($propobj->getLabel() !== null) {
330
                    // only display properties for which we have a label
331
                    $ret[$prop] = $propobj;
332
                }
333
334
                // searching for subproperties of literals too
335
                foreach ($this->graph->allResources($prop, 'rdfs:subPropertyOf') as $subi) {
336
                    $suburi = EasyRdf_Namespace::shorten($subi->getUri()) ? EasyRdf_Namespace::shorten($subi->getUri()) : $subi->getUri();
337
                    $duplicates[$suburi] = $prop;
338
                }
339
340
                // Iterating through every literal and adding these to the data object.
341
                foreach ($this->resource->allLiterals($sprop) as $val) {
342
                    $literal = new ConceptPropertyValueLiteral($val, $prop);
343
                    // only add literals when they match the content/hit language or have no language defined
344
                    if (isset($ret[$prop]) && ($literal->getLang() === $this->clang || $literal->getLang() === null)) {
345
                        $ret[$prop]->addValue($literal);
346
                    }
347
348
                }
349
350
                // Iterating through every resource and adding these to the data object.
351
                foreach ($this->resource->allResources($sprop) as $val) {
352
                    // skipping narrower concepts which are already shown in a collection
353
                    if ($sprop === 'skos:narrower' && array_key_exists($val->getUri(), $in_a_collection)) {
354
                        continue;
355
                    }
356
357
                    // hiding rdf:type property if it's just skos:Concept
358
                    if ($sprop === 'rdf:type' && $val->shorten() === 'skos:Concept') {
359
                        continue;
360
                    }
361
362
                    // handled by getMappingProperties()
363
                    if (in_array($sprop, $this->MAPPING_PROPERTIES)) {
364
                        continue;
365
                    }
366
367 View Code Duplication
                    if (isset($ret[$prop])) {
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...
368
                        $ret[$prop]->addValue(new ConceptPropertyValue($this->model, $this->vocab, $val, $prop, $this->clang), $this->clang);
369
                    }
370
371
                }
372
            }
373
        }
374
        // adding narrowers part of a collection
375
        foreach ($properties as $prop => $values) {
376
            foreach ($values as $value) {
377
                $ret[$prop]->addValue($value, $this->clang);
378
            }
379
        }
380
381
        foreach ($ret as $key => $prop) {
382
            if (sizeof($prop->getValues()) === 0) {
383
                unset($ret[$key]);
384
            }
385
        }
386
387
        $ret = $this->removeDuplicatePropertyValues($ret, $duplicates);
388
        // sorting the properties to the order preferred in the Skosmos concept page.
389
        $ret = $this->arbitrarySort($ret);
390
        return $ret;
391
    }
392
393
    /**
394
     * Removes properties that have duplicate values.
395
     * @param $ret the array of properties generated by getProperties
396
     * @param $duplicates array of properties found are a subProperty of a another property
397
     * @return array of ConceptProperties
398
     */
399
    public function removeDuplicatePropertyValues($ret, $duplicates)
400
    {
401
        $propertyValues = array();
402
403
        foreach ($ret as $prop) {
404
            foreach ($prop->getValues() as $value) {
405
                $label = $value->getLabel();
406
                $propertyValues[(method_exists($label, 'getValue')) ? $label->getValue() : $label][] = $value->getType();
407
            }
408
        }
409
410
        foreach ($propertyValues as $value => $propnames) {
411
            // if there are multiple properties with the same string value.
412
            if (count($propnames) > 1) {
413
                foreach ($propnames as $property) {
414
                    // if there is a more accurate property delete the more generic one.
415
                    if (isset($duplicates[$property])) {
416
                        unset($ret[$property]);
417
                    }
418
                }
419
420
            }
421
        }
422
        return $ret;
423
    }
424
425
    /**
426
     * Gets the creation date and modification date if available.
427
     * @return String containing the date information in a human readable format.
428
     */
429
    public function getDate()
430
    {
431
        $ret = '';
432
        $created = '';
433
        $modified = '';
434
        try {
435
            // finding the created properties
436
            if ($this->resource->get('dc:created')) {
437
                $created = $this->resource->get('dc:created')->getValue();
438
            }
439
440
            // finding the modified properties
441
            if ($this->resource->get('dc:modified')) {
442
                $modified = $this->resource->get('dc:modified')->getValue();
443
            }
444
445
            // making a human readable string from the timestamps
446
            if ($created != '') {
447
                $ret = gettext('skosmos:created') . ' ' . (Punic\Calendar::formatDate($created, 'short'));
448
            }
449
450
            if ($modified != '') {
451
                if ($created != '') {
452
                    $ret .= ', ' . gettext('skosmos:modified') . ' ' . (Punic\Calendar::formatDate($modified, 'short'));
453
                } else {
454
                    $ret .= ' ' . ucfirst(gettext('skosmos:modified')) . ' ' . (Punic\Calendar::formatDate($modified, 'short'));
455
                }
456
457
            }
458
        } catch (Exception $e) {
459
            trigger_error($e->getMessage(), E_USER_WARNING);
460
            $ret = '';
461
            if ($this->resource->get('dc:modified')) {
462
                $modified = (string) $this->resource->get('dc:modified');
463
                $ret = gettext('skosmos:modified') . ' ' . $modified; 
464
            }
465
            if ($this->resource->get('dc:created')) {
466
                $created .= (string) $this->resource->get('dc:created');
467
                $ret .= ' ' . gettext('skosmos:created') . ' ' . $created; 
468
            }
469
        }
470
        return $ret;
471
    }
472
473
    /**
474
     * Gets the members of a specific collection.
475
     * @param $coll
476
     * @param array containing all narrowers as EasyRdf_Resource
477
     * @return array containing ConceptPropertyValue objects
478
     */
479
    private function getCollectionMembers($coll, $narrowers)
480
    {
481
        $members_array = array();
482
        $coll_label = $coll->label()->getValue($this->clang) ? $coll->label($this->clang) : $coll->label();
483
        if ($coll_label) {
484
            $coll_label = $coll_label->getValue();
485
        }
486
487
        $members_array[$coll_label] = new ConceptPropertyValue($this->model, $this->vocab, $coll, 'skos:narrower', $this->clang);
488
        foreach ($coll->allResources('skos:member') as $member) {
489
            if (array_key_exists($member->getUri(), $narrowers)) {
490
                $narrower = $narrowers[$member->getUri()];
491 View Code Duplication
                if (isset($narrower)) {
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...
492
                    $members_array[$coll_label]->addSubMember(new ConceptPropertyValue($this->model, $this->vocab, $narrower, 'skos:member', $this->clang), $this->clang);
493
                }
494
495
            }
496
        }
497
498
        return $members_array;
499
    }
500
501
    /**
502
     * Gets the groups the concept belongs to.
503
     */
504
    public function getGroupProperties()
505
    {
506
        return $this->getReverseResources(false);
507
    }
508
509
    /**
510
     * Gets the groups/arrays the concept belongs to.
511
     */
512
    public function getReverseResources($includeArrays) {
513
        $groups = array();
514
        $reverseResources = $this->graph->resourcesMatching('skos:member', $this->resource);
515
        if (isset($reverseResources)) {
516
            $arrayClassURI = $this->vocab !== null ? $this->vocab->getConfig()->getArrayClassURI() : null;
517
            $arrayClass = $arrayClassURI !== null ? EasyRdf_Namespace::shorten($arrayClassURI) : null;
518
            foreach ($reverseResources as $reverseResource) {
519
                if (in_array($arrayClass, $reverseResource->types()) === $includeArrays) {
520
                    $property = in_array($arrayClass, $reverseResource->types()) ? "skosmos:memberOfArray" : "skosmos:memberOf";
521
                    $coll_label = $reverseResource->label($this->clang) ? $reverseResource->label($this->clang) : $reverseResource->label();
522
                    if ($coll_label) {
523
                        $coll_label = $coll_label->getValue();
524
                    }
525
526
                    $groups[$coll_label] = new ConceptPropertyValue($this->model, $this->vocab, $reverseResource, $property, $this->clang);
527
                    ksort($groups);
528
                    $super = $this->graph->resourcesMatching('skos:member', $reverseResource);
529
                    while (isset($super) && !empty($super)) {
530
                        foreach ($super as $res) {
531
                            $superprop = new ConceptPropertyValue($this->model, $this->vocab, $res, 'skosmos:memberOfSuper', $this->clang);
532
                            array_unshift($groups, $superprop);
533
                            $super = $this->graph->resourcesMatching('skos:member', $res);
534
                        }
535
                    }
536
                }
537
            }
538
        }
539
        return $groups;
540
    }
541
542
    public function getArrayProperties() {
543
        return $this->getReverseResources(true);
544
    }
545
546
    /**
547
     * Gets the values for the property in question in all other languages than the ui language.
548
     */
549
    public function getForeignLabels()
550
    {
551
        $labels = array();
552 View Code Duplication
        foreach ($this->resource->allLiterals('skos:prefLabel') as $lit) {
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...
553
            // filtering away subsets of the current language eg. en vs en-GB
554
            if ($lit->getLang() != $this->clang && strpos($lit->getLang(), $this->getEnvLang() . '-') !== 0) {
555
                $labels[Punic\Language::getName($lit->getLang(), $this->getEnvLang())][] = new ConceptPropertyValueLiteral($lit, 'skos:prefLabel');
556
            }
557
558
        }
559 View Code Duplication
        foreach ($this->resource->allLiterals('skos:altLabel') as $lit) {
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...
560
            // filtering away subsets of the current language eg. en vs en-GB
561
            if ($lit->getLang() != $this->clang && strpos($lit->getLang(), $this->getEnvLang() . '-') !== 0) {
562
                $labels[Punic\Language::getName($lit->getLang(), $this->getEnvLang())][] = new ConceptPropertyValueLiteral($lit, 'skos:altLabel');
563
            }
564
565
        }
566
        ksort($labels);
567
        return $labels;
568
    }
569
570
    /**
571
     * Gets the values for the property in question in all other languages than the ui language.
572
     * @param string $property
573
     */
574
    public function getAllLabels($property)
575
    {
576
        $labels = array();
577
        if (EasyRdf_Namespace::shorten($property) !== null) {
578
            // shortening property labels if possible
579
            $property = EasyRdf_Namespace::shorten($property);
580
        } else {
581
            $property = "<$property>";
582
        }
583
        // EasyRdf requires full URIs to be in angle brackets
584
        foreach ($this->resource->allLiterals($property) as $lit) {
585
            $labels[Punic\Language::getName($lit->getLang(), $this->getEnvLang())][] = new ConceptPropertyValueLiteral($lit, $property);
586
        }
587
        ksort($labels);
588
        return $labels;
589
    }
590
}
591