Concept::getProperties()   F
last analyzed

Complexity

Conditions 53
Paths > 20000

Size

Total Lines 184
Code Lines 93

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 93
dl 0
loc 184
rs 0
c 0
b 0
f 0
cc 53
nc 26874585
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Dataobject for a single concept.
5
 */
6
7
class Concept extends VocabularyDataObject implements Modifiable
8
{
9
    /**
10
     * Stores a label string if the concept has been found through
11
     * a altLabel/label in a another language than the ui.
12
     */
13
    private $foundby;
14
    /** Type of foundby match: 'alt', 'hidden' or 'lang' */
15
    private $foundbytype;
16
    /** the EasyRdf\Graph object of the concept */
17
    private $graph;
18
    private $clang;
19
    private $deleted;
20
21
    /** concept properties that should not be shown to users */
22
    private $DELETED_PROPERTIES = array(
23
        'skosext:broaderGeneric', # these are remnants of bad modeling
24
        'skosext:broaderPartitive', #
25
26
        'skos:hiddenLabel', # because it's supposed to be hidden
27
        'skos:prefLabel', # handled separately by getLabel
28
        'rdfs:label', # handled separately by getLabel
29
30
        'skos:topConceptOf', # because it's too technical, not relevant for users
31
        'skos:inScheme', # should be evident in any case
32
        'skos:member', # this shouldn't be shown on the group page
33
        'dc:created', # handled separately
34
        'dc:modified', # handled separately
35
        'owl:deprecated', # indicated visually
36
    );
37
38
    /** related concepts that should be shown to users in the appendix */
39
    private $MAPPING_PROPERTIES = array(
40
        'skos:exactMatch',
41
        'skos:narrowMatch',
42
        'skos:broadMatch',
43
        'skos:closeMatch',
44
        'skos:relatedMatch',
45
        'rdfs:seeAlso',
46
        'owl:sameAs',
47
    );
48
49
    /** default external properties we are interested in saving/displaying from mapped external objects */
50
    private $DEFAULT_EXT_PROPERTIES = array(
51
        "dc11:title",
52
        "dcterms:title",
53
        "skos:prefLabel",
54
        "skos:exactMatch",
55
        "skos:closeMatch",
56
        "skos:inScheme",
57
        "rdfs:label",
58
        "rdfs:isDefinedBy",
59
        "owl:sameAs",
60
        "rdf:type",
61
        "void:inDataset",
62
        "void:sparqlEndpoint",
63
        "void:uriLookupEndpoint",
64
        "schema:about",
65
        "schema:description",
66
        "schema:inLanguage",
67
        "schema:name",
68
        "schema:isPartOf",
69
        "wdt:P31",
70
        "wdt:P625"
71
    );
72
73
    /**
74
     * Initializing the concept object requires the following parameters.
75
     * @param Model $model
76
     * @param Vocabulary $vocab
77
     * @param EasyRdf\Resource $resource
78
     * @param EasyRdf\Graph $graph
79
     */
80
    public function __construct($model, $vocab, $resource, $graph, $clang)
81
    {
82
        parent::__construct($model, $vocab, $resource);
83
        $this->deleted = $this->DELETED_PROPERTIES;
84
        if ($vocab !== null) {
85
            $this->order = $vocab->getConfig()->getPropertyOrder();
86
            // delete notations unless configured to show them
87
            if (!$vocab->getConfig()->getShowNotationAsProperty()) {
88
                $this->deleted[] = 'skos:notation';
89
            }
90
        } else {
91
            $this->order = VocabularyConfig::DEFAULT_PROPERTY_ORDER;
92
        }
93
        $this->graph = $graph;
94
        $this->clang = $clang;
95
    }
96
97
    /**
98
     * Returns the concept uri.
99
     * @return string
100
     */
101
    public function getUri()
102
    {
103
        return $this->resource->getUri();
104
    }
105
106
    public function getType()
107
    {
108
        return $this->resource->types();
109
    }
110
111
112
    /**
113
     * Returns a boolean value indicating whether the resource is a group defined in the vocab config as skosmos:groupClass.
114
     * @return boolean
115
     */
116
    public function isGroup()
117
    {
118
        $groupClass = $this->getVocab()->getConfig()->getGroupClassURI();
119
        if ($groupClass) {
120
            $groupClass = EasyRdf\RdfNamespace::shorten($groupClass) !== null ? EasyRdf\RdfNamespace::shorten($groupClass) : $groupClass;
0 ignored issues
show
introduced by
The condition EasyRdf\RdfNamespace::sh...n($groupClass) !== null is always true.
Loading history...
121
            return in_array($groupClass, $this->getType());
122
        }
123
        return false;
124
    }
125
126
    /**
127
     * Returns a boolean value indicating if the concept has been deprecated.
128
     * @return boolean
129
     */
130
    public function getDeprecated()
131
    {
132
        $deprecatedValue = $this->resource->getLiteral('owl:deprecated');
133
        return ($deprecatedValue !== null && filter_var($deprecatedValue->getValue(), FILTER_VALIDATE_BOOLEAN));
134
    }
135
136
    /**
137
     * Returns a label for the concept in the content language or if not possible in any language.
138
     * @return string
139
     */
140
    public function getLabel()
141
    {
142
        foreach ($this->vocab->getConfig()->getLanguageOrder($this->clang) as $fallback) {
143
            if ($this->resource->label($fallback) !== null) {
144
                return $this->resource->label($fallback);
145
            }
146
            // We need to check all the labels in case one of them matches a subtag of the current language
147
            foreach ($this->resource->allLiterals('skos:prefLabel') as $label) {
148
                // the label lang code is a subtag of the UI lang eg. en-GB - create a new literal with the main language
149
                if ($label !== null && strpos($label->getLang(), $fallback . '-') === 0) {
150
                    return EasyRdf\Literal::create($label, $fallback);
151
                }
152
            }
153
        }
154
155
        // Last resort: label in any language, including literal with empty language tag
156
        $label = $this->resource->label();
157
        if ($label !== null) {
158
            if (!$label->getLang()) {
159
                return $label->getValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $label->getValue() also could return the type DateTime|boolean which is incompatible with the documented return type string.
Loading history...
160
            }
161
            return EasyRdf\Literal::create($label->getValue(), $label->getLang());
162
        }
163
164
        // empty
165
        return "";
166
    }
167
168
    public function hasXlLabel()
169
    {
170
        $xlLabel = $this->getXlLabel();
171
        return ($xlLabel !== null && !empty($xlLabel->getProperties()));
172
    }
173
174
    public function getXlLabel()
175
    {
176
        $labels = $this->resource->allResources('skosxl:prefLabel');
177
        foreach ($labels as $labres) {
178
            $label = $labres->getLiteral('skosxl:literalForm');
179
            if ($label !== null && $label->getLang() == $this->clang) {
180
                return new LabelSkosXL($this->model, $labres);
181
            }
182
        }
183
        return null;
184
    }
185
186
    /**
187
     * Returns a notation for the concept or null if it has not been defined.
188
     * @return string eg. '999'
189
     */
190
    public function getNotation()
191
    {
192
        $notation = $this->resource->get('skos:notation');
193
        if ($this->vocab->getConfig()->showNotation() && $notation !== null) {
194
            return $notation->getValue();
195
        }
196
197
        return null;
198
    }
199
200
    /**
201
     * Returns the Vocabulary object or undefined if that is not available.
202
     * @return Vocabulary
203
     */
204
    public function getVocab()
205
    {
206
        return $this->vocab;
207
    }
208
209
    /**
210
     * Returns the vocabulary shortname string or id if that is not available.
211
     * @return string
212
     */
213
    public function getShortName()
214
    {
215
        return $this->vocab ? $this->vocab->getShortName() : null;
216
    }
217
218
    /**
219
     * Returns the vocabulary shortname string or id if that is not available.
220
     * @return string
221
     */
222
    public function getVocabTitle()
223
    {
224
        return $this->vocab ? $this->vocab->getTitle() : null;
225
    }
226
227
    /**
228
     * Setter for the $clang property.
229
     * @param string $clang language code eg. 'en'
230
     */
231
    public function setContentLang($clang)
232
    {
233
        $this->clang = $clang;
234
    }
235
236
    public function getContentLang()
237
    {
238
        return $this->clang;
239
    }
240
241
    /**
242
     * Setter for the $foundby property.
243
     * @param string $label label that was matched
244
     * @param string $type type of match: 'alt', 'hidden', or 'lang'
245
     */
246
    public function setFoundBy($label, $type)
247
    {
248
        $this->foundby = $label;
249
        $this->foundbytype = $type;
250
    }
251
252
    /**
253
     * Getter for the $foundby property.
254
     * @return string
255
     */
256
    public function getFoundBy()
257
    {
258
        return $this->foundby;
259
    }
260
261
    /**
262
     * Getter for the $foundbytype property.
263
     * @return string
264
     */
265
    public function getFoundByType()
266
    {
267
        return $this->foundbytype;
268
    }
269
270
    /**
271
     * Processes a single external resource i.e., adds the properties from
272
     * 1) $this->$DEFAULT_EXT_PROPERTIES
273
     * 2) VocabConfig external properties
274
     * 3) Possible plugin defined external properties
275
     * to $this->graph
276
     * @param EasyRdf\Resource $res
277
     */
278
    public function processExternalResource($res)
279
    {
280
        $exGraph = $res->getGraph();
281
        // catch external subjects that have $res as object
282
        $extSubjects = $exGraph->resourcesMatching("schema:about", $res);
283
284
        $propList =  array_unique(array_merge(
285
            $this->DEFAULT_EXT_PROPERTIES,
286
            $this->getVocab()->getConfig()->getExtProperties(),
287
            $this->getVocab()->getConfig()->getPluginRegister()->getExtProperties()
288
        ));
289
290
        $seen = array();
291
        $this->addExternalTriplesToGraph($res, $seen, $propList);
292
        foreach ($extSubjects as $extSubject) {
293
            if ($extSubject->isBNode() && array_key_exists($extSubject->getUri(), $seen)) {
294
                // already processed, skip
295
                continue;
296
            }
297
            $seen[$extSubject->getUri()] = 1;
298
            $this->addExternalTriplesToGraph($extSubject, $seen, $propList);
299
        }
300
301
    }
302
303
    /**
304
     * Adds resource properties to $this->graph
305
     * @param EasyRdf\Resource $res
306
     * @param string[] $seen Processed resources so far
307
     * @param string[] $props (optional) limit to these property URIs
308
     */
309
    private function addExternalTriplesToGraph($res, &$seen, $props = null)
310
    {
311
        if (array_key_exists($res->getUri(), $seen) && $seen[$res->getUri()] === 0) {
0 ignored issues
show
introduced by
The condition $seen[$res->getUri()] === 0 is always false.
Loading history...
312
            return;
313
        }
314
        $seen[$res->getUri()] = 0;
315
316
        if ($res->isBNode() || is_null($props)) {
317
            foreach ($res->propertyUris() as $prop) {
318
                $this->addPropertyValues($res, $prop, $seen);
319
            }
320
        } else {
321
            foreach ($props as $prop) {
322
                if ($res->hasProperty($prop)) {
323
                    $this->addPropertyValues($res, $prop, $seen);
324
                }
325
            }
326
        }
327
    }
328
329
    /**
330
     * Adds values of a single single property of a resource to $this->graph
331
     * implements Concise Bounded Description definition
332
     * @param EasyRdf\Resource $res
333
     * @param string $prop
334
     * @param string[] $seen Processed resources so far
335
     */
336
    private function addPropertyValues($res, $prop, &$seen)
337
    {
338
        $resList = $res->allResources('<' . $prop . '>');
339
340
        foreach ($resList as $res2) {
341
            if ($res2->isBNode()) {
342
                $this->addExternalTriplesToGraph($res2, $seen);
343
            }
344
            $this->graph->addResource($res, $prop, $res2);
345
            $this->addResourceReifications($res, $prop, $res2, $seen);
346
        }
347
348
        $litList = $res->allLiterals('<' . $prop . '>');
349
350
        foreach ($litList as $lit) {
351
            $this->graph->addLiteral($res, $prop, $lit);
352
            $this->addLiteralReifications($res, $prop, $lit, $seen);
353
        }
354
    }
355
356
    /**
357
     * Adds reifications of a triple having a literal object to $this->graph
358
     * @param EasyRdf\Resource $sub
359
     * @param string $pred
360
     * @param EasyRdf\Literal $obj
361
     * @param string[] $seen Processed resources so far
362
     */
363
    private function addLiteralReifications($sub, $pred, $obj, &$seen)
364
    {
365
        $pos_reifs = $sub->getGraph()->resourcesMatching("rdf:subject", $sub);
366
        foreach ($pos_reifs as $pos_reif) {
367
            $lit = $pos_reif->getLiteral("rdf:object", $obj->getLang());
368
369
            if (!is_null($lit) && $lit->getValue() === $obj->getValue() &&
370
                $pos_reif->isA("rdf:Statement") &&
371
                $pos_reif->hasProperty("rdf:predicate", new EasyRdf\Resource($pred, $sub->getGraph()))) {
372
                $this->addExternalTriplesToGraph($pos_reif, $seen);
373
            }
374
        }
375
    }
376
377
    /**
378
     * Adds reifications of a triple having a resource object to $this->graph
379
     * @param EasyRdf\Resource $sub
380
     * @param string $pred
381
     * @param EasyRdf\Resource $obj
382
     * @param string[] $seen Processed resources so far
383
     */
384
    private function addResourceReifications($sub, $pred, $obj, &$seen)
385
    {
386
        $pos_reifs = $sub->getGraph()->resourcesMatching("rdf:subject", $sub);
387
        foreach ($pos_reifs as $pos_reif) {
388
            if ($pos_reif->isA("rdf:Statement") &&
389
                $pos_reif->hasProperty("rdf:object", $obj) &&
390
                $pos_reif->hasProperty("rdf:predicate", new EasyRdf\Resource($pred, $sub->getGraph()))) {
391
                $this->addExternalTriplesToGraph($pos_reif, $seen);
392
            }
393
        }
394
    }
395
396
    /**
397
     * @return ConceptProperty[]
398
     */
399
    public function getMappingProperties(array $whitelist = null)
400
    {
401
        $ret = array();
402
403
        $longUris = $this->resource->propertyUris();
404
        foreach ($longUris as &$prop) {
405
            if (EasyRdf\RdfNamespace::shorten($prop) !== null) {
406
                // shortening property labels if possible
407
                $prop = $sprop = EasyRdf\RdfNamespace::shorten($prop);
408
            } else {
409
                // EasyRdf requires full URIs to be in angle brackets
410
                $sprop = "<$prop>";
411
            }
412
            if ($whitelist && !in_array($prop, $whitelist)) {
413
                // whitelist in use and this is not a whitelisted property, skipping
414
                continue;
415
            }
416
417
            if (in_array($prop, $this->MAPPING_PROPERTIES) && !in_array($prop, $this->DELETED_PROPERTIES)) {
418
                $propres = new EasyRdf\Resource($prop, $this->graph);
419
                $proplabel = $propres->label($this->getLang()) ? $propres->label($this->getLang()) : $propres->label(); // current language
420
                $propobj = new ConceptProperty($this->model, $prop, $proplabel);
0 ignored issues
show
Bug introduced by
$this->model of type Model is incompatible with the type string expected by parameter $model of ConceptProperty::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

420
                $propobj = new ConceptProperty(/** @scrutinizer ignore-type */ $this->model, $prop, $proplabel);
Loading history...
421
                if ($propobj->getLabel() !== null) {
422
                    // only display properties for which we have a label
423
                    $ret[$prop] = $propobj;
424
                }
425
426
                // Iterating through every resource and adding these to the data object.
427
                foreach ($this->resource->allResources($sprop) as $val) {
428
                    if (isset($ret[$prop])) {
429
                        // checking if the target vocabulary can be found at the skosmos endpoint
430
                        $exuri = $val->getUri();
431
                        // if multiple vocabularies are found, the following method will return in priority the current vocabulary of the concept
432
                        $exvoc = $this->model->guessVocabularyFromURI($exuri, $this->vocab->getId());
433
                        // if not querying the uri itself
434
                        if (!$exvoc) {
435
                            $response = null;
436
                            // if told to do so in the vocabulary configuration
437
                            if ($this->vocab->getConfig()->getExternalResourcesLoading()) {
438
                                $response = $this->model->getResourceFromUri($exuri);
439
                            }
440
441
                            if ($response) {
442
                                $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $response, $this->resource, $prop));
443
444
                                $this->processExternalResource($response);
445
446
                                continue;
447
                            }
448
                        }
449
                        $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $val, $this->resource, $prop, $this->clang));
450
                    }
451
                }
452
            }
453
        }
454
455
        // sorting the properties to a order preferred in the Skosmos concept page.
456
        return $this->arbitrarySort($ret);
457
    }
458
459
    /**
460
     * Iterates over all the properties of the concept and returns those in an array.
461
     * @return array
462
     */
463
    public function getProperties()
464
    {
465
        $properties = array();
466
        $narrowersByUri = array();
467
        $inCollection = array();
468
        $membersArray = array();
469
        $longUris = $this->resource->propertyUris();
470
        $duplicates = array();
471
        $ret = array();
472
473
        // looking for collections and linking those with their narrower concepts
474
        if ($this->vocab->getConfig()->getArrayClassURI() !== null) {
0 ignored issues
show
introduced by
The condition $this->vocab->getConfig(...rrayClassURI() !== null is always true.
Loading history...
475
            $collections = $this->graph->allOfType($this->vocab->getConfig()->getArrayClassURI());
476
            if (sizeof($collections) > 0) {
477
                // indexing the narrowers once to avoid iterating all of them with every collection
478
                foreach ($this->resource->allResources('skos:narrower') as $narrower) {
479
                    $narrowersByUri[$narrower->getUri()] = $narrower;
480
                }
481
482
                foreach ($collections as $coll) {
483
                    $currCollMembers = $this->getCollectionMembers($coll, $narrowersByUri);
484
                    foreach ($currCollMembers as $collection) {
485
                        if ($collection->getSubMembers()) {
486
                            $submembers = $collection->getSubMembers();
487
                            foreach ($submembers as $member) {
488
                                $inCollection[$member->getUri()] = true;
489
                            }
490
491
                        }
492
                    }
493
494
                    if (isset($collection) && $collection->getSubMembers()) {
495
                        $membersArray = array_merge($currCollMembers, $membersArray);
496
                    }
497
498
                }
499
                $properties['skos:narrower'] = $membersArray;
500
            }
501
        }
502
503
        foreach ($longUris as &$prop) {
504
            // storing full URI without brackets in a separate variable
505
            $longUri = $prop;
506
            if (EasyRdf\RdfNamespace::shorten($prop) !== null) {
507
                // shortening property labels if possible
508
                $prop = $sprop = EasyRdf\RdfNamespace::shorten($prop);
509
            } else {
510
                // EasyRdf requires full URIs to be in angle brackets
511
                $sprop = "<$prop>";
512
            }
513
514
            if (!in_array($prop, $this->deleted) || ($this->isGroup() === false && $prop === 'skos:member')) {
0 ignored issues
show
Bug introduced by
$this->deleted of type mixed is incompatible with the type array expected by parameter $haystack of in_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

514
            if (!in_array($prop, /** @scrutinizer ignore-type */ $this->deleted) || ($this->isGroup() === false && $prop === 'skos:member')) {
Loading history...
515
                // retrieve property label and super properties from the current vocabulary first
516
                $propres = new EasyRdf\Resource($prop, $this->graph);
517
                $proplabel = $propres->label($this->getLang()) ? $propres->label($this->getLang()) : $propres->label();
518
519
                $prophelp = $propres->getLiteral('rdfs:comment|skos:definition', $this->getLang());
520
                if ($prophelp === null) {
521
                    // try again without language restriction
522
                    $prophelp = $propres->getLiteral('rdfs:comment|skos:definition');
523
                }
524
525
                // check if the property is one of the well-known properties for which we have a translation
526
                // if it is then we can skip the additional lookups in the default graph
527
                $propkey = (substr($prop, 0, 5) == 'dc11:') ?
528
                    str_replace('dc11:', 'dc:', $prop) : $prop;
529
                $is_well_known = ($this->model->getText($propkey) != $propkey);
530
531
                // if not found in current vocabulary, look up in the default graph to be able
532
                // to read an ontology loaded in a separate graph
533
                // note that this imply that the property has an rdf:type declared for the query to work
534
                if (!$is_well_known && !$proplabel) {
535
                    $envLangLabels = $this->model->getDefaultSparql()->queryLabel($longUri, $this->getLang());
536
537
                    $defaultPropLabel = $this->model->getDefaultSparql()->queryLabel($longUri, '');
538
539
                    if ($envLangLabels) {
540
                        $proplabel = $envLangLabels[$this->getLang()];
541
                    } else {
542
                        if ($defaultPropLabel) {
543
                            $proplabel = $defaultPropLabel[''];
544
                        }
545
                    }
546
                }
547
548
                // look for superproperties in the current graph
549
                $superprops = array();
550
                foreach ($this->graph->allResources($prop, 'rdfs:subPropertyOf') as $subi) {
551
                    $superprops[] = $subi->getUri();
552
                }
553
554
                // also look up superprops in the default graph if not found in current vocabulary
555
                if (!$is_well_known && (!$superprops || empty($superprops))) {
556
                    $superprops = $this->model->getDefaultSparql()->querySuperProperties($longUri);
557
                }
558
559
                // we're reading only one super property, even if there are multiple ones
560
                $superprop = ($superprops) ? $superprops[0] : null;
561
                if ($superprop) {
562
                    $superprop = EasyRdf\RdfNamespace::shorten($superprop) ? EasyRdf\RdfNamespace::shorten($superprop) : $superprop;
563
                }
564
                $sort_by_notation = $this->vocab->getConfig()->getSortByNotation();
565
                $propobj = new ConceptProperty($this->model, $prop, $proplabel, $prophelp, $superprop, $sort_by_notation);
0 ignored issues
show
Bug introduced by
It seems like $sort_by_notation can also be of type string; however, parameter $sort_by_notation of ConceptProperty::__construct() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

565
                $propobj = new ConceptProperty($this->model, $prop, $proplabel, $prophelp, $superprop, /** @scrutinizer ignore-type */ $sort_by_notation);
Loading history...
Bug introduced by
$this->model of type Model is incompatible with the type string expected by parameter $model of ConceptProperty::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

565
                $propobj = new ConceptProperty(/** @scrutinizer ignore-type */ $this->model, $prop, $proplabel, $prophelp, $superprop, $sort_by_notation);
Loading history...
566
567
                if ($propobj->getLabel() !== null) {
568
                    // only display properties for which we have a label
569
                    $ret[$prop] = $propobj;
570
                }
571
572
                // searching for subproperties of literals too
573
                if ($superprops) {
574
                    foreach ($superprops as $subi) {
575
                        $suburi = EasyRdf\RdfNamespace::shorten($subi) ? EasyRdf\RdfNamespace::shorten($subi) : $subi;
576
                        $duplicates[$suburi] = $prop;
577
                    }
578
                }
579
580
                // Iterating through every literal and adding these to the data object.
581
                foreach ($this->resource->allLiterals($sprop) as $val) {
582
                    $literal = new ConceptPropertyValueLiteral($this->model, $this->vocab, $this->resource, $val, $prop);
583
                    // only add literals when they match the content/hit language or have no language defined OR when they are literals of a multilingual property
584
                    if (isset($ret[$prop]) && ($literal->getLang() === $this->clang || $literal->getLang() === null) || $this->vocab->getConfig()->hasMultiLingualProperty($prop)) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (IssetNode && $literal->...iLingualProperty($prop), Probably Intended Meaning: IssetNode && ($literal->...LingualProperty($prop))
Loading history...
585
                        $ret[$prop]->addValue($literal);
586
                    }
587
                }
588
589
                // Iterating through every resource and adding these to the data object.
590
                foreach ($this->resource->allResources($sprop) as $val) {
591
                    // skipping narrower concepts which are already shown in a collection
592
                    if ($sprop === 'skos:narrower' && array_key_exists($val->getUri(), $inCollection)) {
593
                        continue;
594
                    }
595
596
                    // hiding rdf:type property if it's just skos:Concept
597
                    if ($sprop === 'rdf:type' && $val->shorten() === 'skos:Concept') {
598
                        continue;
599
                    }
600
601
                    // handled by getMappingProperties()
602
                    if (in_array($sprop, $this->MAPPING_PROPERTIES)) {
603
                        continue;
604
                    }
605
606
                    if (isset($ret[$prop])) {
607
                        // create a ConceptPropertyValue first, assuming the resource exists in current vocabulary
608
                        $value = new ConceptPropertyValue($this->model, $this->vocab, $val, $prop, $this->clang);
609
                        $ret[$prop]->addValue($value);
610
                    }
611
612
                }
613
            }
614
        }
615
        // adding narrowers part of a collection
616
        foreach ($properties as $prop => $values) {
617
            foreach ($values as $value) {
618
                $ret[$prop]->addValue($value);
619
            }
620
        }
621
622
        $groupPropObj = new ConceptProperty($this->model, 'skosmos:memberOf', null);
623
        foreach ($this->getGroupProperties() as $propVals) {
624
            foreach ($propVals as $propVal) {
625
                $groupPropObj->addValue($propVal);
626
            }
627
        }
628
        $ret['skosmos:memberOf'] = $groupPropObj;
629
630
        $arrayPropObj = new ConceptProperty($this->model, 'skosmos:memberOfArray', null);
631
        foreach ($this->getArrayProperties() as $propVals) {
632
            foreach ($propVals as $propVal) {
633
                $arrayPropObj->addValue($propVal);
634
            }
635
        }
636
        $ret['skosmos:memberOfArray'] = $arrayPropObj;
637
638
        foreach ($ret as $key => $prop) {
639
            if (sizeof($prop->getValues()) === 0) {
640
                unset($ret[$key]);
641
            }
642
        }
643
644
        $ret = $this->removeDuplicatePropertyValues($ret, $duplicates);
645
        // sorting the properties to the order preferred in the Skosmos concept page.
646
        return $this->arbitrarySort($ret);
647
    }
648
649
    /**
650
     * Removes properties that have duplicate values.
651
     * @param array $ret the array of properties generated by getProperties
652
     * @param array $duplicates array of properties found are a subProperty of a another property
653
     * @return array of ConceptProperties
654
     */
655
    public function removeDuplicatePropertyValues($ret, $duplicates)
656
    {
657
        $propertyValues = array();
658
659
        foreach ($ret as $prop) {
660
            foreach ($prop->getValues() as $value) {
661
                $label = $value->getLabel();
662
                $propertyValues[(is_object($label) && method_exists($label, 'getValue')) ? $label->getValue() : $label][] = $value->getType();
663
            }
664
        }
665
666
        foreach ($propertyValues as $value => $propnames) {
667
            // if there are multiple properties with the same string value.
668
            if (count($propnames) > 1) {
669
                foreach ($propnames as $property) {
670
                    // if there is a more accurate property delete the more generic one.
671
                    if (isset($duplicates[$property])) {
672
                        unset($ret[$property]);
673
                    }
674
                }
675
676
            }
677
        }
678
        // handled separately: remove duplicate skos:prefLabel value (#854)
679
        if (isset($duplicates["skos:prefLabel"])) {
680
            unset($ret[$duplicates["skos:prefLabel"]]);
681
        }
682
        return $ret;
683
    }
684
685
    /**
686
     * @param $lang UI language
0 ignored issues
show
Bug introduced by
The type UI was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
687
     * @return String|null the translated label of skos:prefLabel subproperty, or null if not available
688
     */
689
    public function getPreferredSubpropertyLabelTranslation($lang)
690
    {
691
        $prefLabelProp = $this->graph->resource("skos:prefLabel");
692
        $subPrefLabelProps = $this->graph->resourcesMatching('rdfs:subPropertyOf', $prefLabelProp);
693
        foreach ($subPrefLabelProps as $subPrefLabelProp) {
694
            // return the first available translation
695
            if ($subPrefLabelProp->label($lang)) {
696
                return $subPrefLabelProp->label($lang);
697
            }
698
        }
699
        return null;
700
    }
701
702
    /**
703
     * @return DateTime|null the modified date, or null if not available
704
     */
705
    public function getModifiedDate()
706
    {
707
        // finding the modified properties
708
        /** @var \EasyRdf\Resource|\EasyRdf\Literal|null $modifiedResource */
709
        $modifiedResource = $this->resource->get('dc:modified');
710
        if ($modifiedResource && $modifiedResource instanceof \EasyRdf\Literal\Date) {
711
            return $modifiedResource->getValue();
712
        }
713
714
        // if the concept does not have a modified date, we look for it in its
715
        // vocabulary
716
        return $this->getVocab()->getModifiedDate();
717
    }
718
719
    /**
720
     * Gets the creation date and modification date if available.
721
     * @return String containing the date information in a human readable format.
722
     */
723
    public function getDate()
724
    {
725
        $ret = '';
726
        $created = '';
727
        try {
728
            // finding the created properties
729
            if ($this->resource->get('dc:created')) {
730
                $created = $this->resource->get('dc:created')->getValue();
731
            }
732
733
            $modified = $this->getModifiedDate();
734
735
            // making a human readable string from the timestamps
736
            if ($created != '') {
737
                $ret = $this->model->getText('skosmos:created') . ' ' . (Punic\Calendar::formatDate($created, 'short', $this->getLang()));
738
            }
739
740
            if ($modified != '') {
741
                if ($created != '') {
742
                    $ret .= ', ' . $this->model->getText('skosmos:modified') . ' ' . (Punic\Calendar::formatDate($modified, 'short', $this->getLang()));
743
                } else {
744
                    $ret .= ' ' . ucfirst($this->model->getText('skosmos:modified')) . ' ' . (Punic\Calendar::formatDate($modified, 'short', $this->getLang()));
745
                }
746
747
            }
748
        } catch (Exception $e) {
749
            trigger_error($e->getMessage(), E_USER_WARNING);
750
            $ret = '';
751
            if ($this->resource->get('dc:modified')) {
752
                $modified = (string) $this->resource->get('dc:modified');
753
                $ret = $this->model->getText('skosmos:modified') . ' ' . $modified;
754
            }
755
            if ($this->resource->get('dc:created')) {
756
                $created .= (string) $this->resource->get('dc:created');
757
                $ret .= ' ' . $this->model->getText('skosmos:created') . ' ' . $created;
758
            }
759
        }
760
        return $ret;
761
    }
762
763
    /**
764
     * Gets the members of a specific collection.
765
     * @param $coll
766
     * @param array containing all narrowers as EasyRdf\Resource
0 ignored issues
show
Bug introduced by
The type containing was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
767
     * @return array containing ConceptPropertyValue objects
768
     */
769
    private function getCollectionMembers($coll, $narrowers)
770
    {
771
        $membersArray = array();
772
        if ($coll->label()) {
773
            $collLabel = $coll->label()->getValue($this->clang) ? $coll->label($this->clang) : $coll->label();
774
            if ($collLabel) {
775
                $collLabel = $collLabel->getValue();
776
            }
777
        } else {
778
            $collLabel = $coll->getUri();
779
        }
780
        $membersArray[$collLabel] = new ConceptPropertyValue($this->model, $this->vocab, $coll, 'skos:narrower', $this->clang);
781
        foreach ($coll->allResources('skos:member') as $member) {
782
            if (array_key_exists($member->getUri(), $narrowers)) {
783
                $narrower = $narrowers[$member->getUri()];
784
                if (isset($narrower)) {
785
                    $membersArray[$collLabel]->addSubMember(new ConceptPropertyValue($this->model, $this->vocab, $narrower, 'skos:member', $this->clang), $this->clang);
786
                }
787
788
            }
789
        }
790
791
        return $membersArray;
792
    }
793
794
    /**
795
     * Gets the groups the concept belongs to.
796
     */
797
    public function getGroupProperties()
798
    {
799
        return $this->getCollections(false);
800
    }
801
802
    /**
803
     * Gets the groups/arrays the concept belongs to.
804
     */
805
    private function getCollections($includeArrays)
806
    {
807
        $groups = array();
808
        $collections = $this->graph->resourcesMatching('skos:member', $this->resource);
809
        if (isset($collections)) {
810
            $arrayClassURI = $this->vocab !== null ? $this->vocab->getConfig()->getArrayClassURI() : null;
811
            $arrayClass = $arrayClassURI !== null ? EasyRdf\RdfNamespace::shorten($arrayClassURI) : null;
812
            $superGroups = $this->resource->all('isothes:superGroup');
813
            $superGroupUris = array_map(function ($obj) { return $obj->getUri(); }, $superGroups);
814
            foreach ($collections as $collection) {
815
                if (in_array($arrayClass, $collection->types()) === $includeArrays) {
816
                    // not adding the memberOf if the reverse resource is already covered by isothes:superGroup see issue #433
817
                    if (in_array($collection->getUri(), $superGroupUris)) {
818
                        continue;
819
                    }
820
                    $property = in_array($arrayClass, $collection->types()) ? "skosmos:memberOfArray" : "skosmos:memberOf";
821
                    $collLabel = $collection->label($this->clang) ? $collection->label($this->clang) : $collection->label();
822
                    if ($collLabel) {
823
                        $collLabel = $collLabel->getValue();
824
                    }
825
826
                    $group = new ConceptPropertyValue($this->model, $this->vocab, $collection, $property, $this->clang);
827
                    $groups[$collLabel] = array($group);
828
829
                    $res = $collection;
830
                    while ($super = $this->graph->resourcesMatching('skos:member', $res)) {
831
                        foreach ($super as $res) {
832
                            $superprop = new ConceptPropertyValue($this->model, $this->vocab, $res, 'skosmos:memberOfSuper', $this->clang);
833
                            array_unshift($groups[$collLabel], $superprop);
834
                        }
835
                    }
836
                }
837
            }
838
        }
839
        uksort($groups, 'strcoll');
840
        return $groups;
841
    }
842
843
    public function getArrayProperties()
844
    {
845
        return $this->getCollections(true);
846
    }
847
848
    /**
849
     * Given a language code, gets its name in UI language via Punic or alternatively
850
     * tries to search for getText translation from Model.
851
     * @param string $langCode
852
     * @return string e.g. 'English'
853
     */
854
    private function langToString($langCode)
855
    {
856
        // using empty string as the language name when there is no langcode set
857
        $langName = '';
858
        if (!empty($langCode)) {
859
            $langName = Punic\Language::getName($langCode, $this->getLang()) !== $langCode ? Punic\Language::getName($langCode, $this->getLang()) : $this->model->getText($langCode);
860
        }
861
        return $langName;
862
    }
863
864
    /**
865
     * Gets the values of a property in all other languages than in the current language
866
     * and places them in a [langCode][userDefinedKey] array.
867
     * @param string $prop The property for which the values are looked upon to
868
     * @param string $key User-defined key for accessing the values
869
     * @return array LangCode-based multidimensional array ([string][string][ConceptPropertyValueLiteral]) or empty array if no values
870
     */
871
    private function getForeignLabelList($prop, $key)
872
    {
873
        $ret = array();
874
        $labels = $this->resource->allLiterals($prop);
875
876
        foreach ($labels as $lit) {
877
            // filtering away subsets of the current language eg. en vs en-GB
878
            $langCode = strval($lit->getLang());
879
            if ($langCode != $this->clang && strpos($langCode, $this->getLang() . '-') !== 0) {
880
                $ret[$langCode][$key][] = new ConceptPropertyValueLiteral($this->model, $this->vocab, $this->resource, $lit, $prop);
881
            }
882
        }
883
        return $ret;
884
    }
885
886
    /**
887
     * Gets the values of skos:prefLabel and skos:altLabel in all other languages than in the current language.
888
     * @return array Language-based multidimensional sorted array ([string][string][ConceptPropertyValueLiteral]) or empty array if no values
889
     */
890
    public function getForeignLabels()
891
    {
892
        $prefLabels = $this->getForeignLabelList('skos:prefLabel', 'prefLabel');
893
        $altLabels = $this->getForeignLabelList('skos:altLabel', 'altLabel');
894
        $ret = array_merge_recursive($prefLabels, $altLabels);
895
896
        $langArray = array_keys($ret);
897
        foreach ($langArray as $lang) {
898
            $coll = collator_create($lang);
899
            if (isset($ret[$lang]['prefLabel'])) {
900
                $coll->sort($ret[$lang]['prefLabel'], Collator::SORT_STRING);
901
            }
902
            if (isset($ret[$lang]['altLabel'])) {
903
                $coll->sort($ret[$lang]['altLabel'], Collator::SORT_STRING);
904
            }
905
            if ($lang !== '') {
906
                $ret[$this->langToString($lang)] = $ret[$lang];
907
                unset($ret[$lang]);
908
            }
909
        }
910
        uksort($ret, 'strcoll');
911
        return $ret;
912
    }
913
914
    /**
915
     * Gets the values for the property in question in all other languages than the ui language.
916
     * @param string $property
917
     * @return array array of labels for the values of the given property
918
     */
919
    public function getAllLabels($property)
920
    {
921
        $labels = array();
922
        // shortening property labels if possible, EasyRdf requires full URIs to be in angle brackets
923
        $property = (EasyRdf\RdfNamespace::shorten($property) !== null) ? EasyRdf\RdfNamespace::shorten($property) : "<$property>";
0 ignored issues
show
introduced by
The condition EasyRdf\RdfNamespace::shorten($property) !== null is always true.
Loading history...
924
        foreach ($this->resource->allLiterals($property) as $lit) {
925
            $labels[Punic\Language::getName($lit->getLang(), $this->getLang())][] = new ConceptPropertyValueLiteral($this->model, $this->vocab, $this->resource, $lit, $property);
926
        }
927
        ksort($labels);
928
        return $labels;
929
    }
930
931
    /**
932
     * Dump concept graph as JSON-LD.
933
     */
934
    public function dumpJsonLd()
935
    {
936
        $context = array(
937
            'skos' => EasyRdf\RdfNamespace::get("skos"),
938
            'isothes' => EasyRdf\RdfNamespace::get("isothes"),
939
            'rdfs' => EasyRdf\RdfNamespace::get("rdfs"),
940
            'owl' => EasyRdf\RdfNamespace::get("owl"),
941
            'dct' => EasyRdf\RdfNamespace::get("dcterms"),
942
            'dc11' => EasyRdf\RdfNamespace::get("dc11"),
943
            'uri' => '@id',
944
            'type' => '@type',
945
            'lang' => '@language',
946
            'value' => '@value',
947
            'graph' => '@graph',
948
            'label' => 'rdfs:label',
949
            'prefLabel' => 'skos:prefLabel',
950
            'altLabel' => 'skos:altLabel',
951
            'hiddenLabel' => 'skos:hiddenLabel',
952
            'broader' => 'skos:broader',
953
            'narrower' => 'skos:narrower',
954
            'related' => 'skos:related',
955
            'inScheme' => 'skos:inScheme',
956
            'schema' => EasyRdf\RdfNamespace::get("schema"),
957
            'wd' => EasyRdf\RdfNamespace::get("wd"),
958
            'wdt' => EasyRdf\RdfNamespace::get("wdt"),
959
        );
960
        $vocabPrefix = preg_replace('/\W+/', '', $this->vocab->getId()); // strip non-word characters
961
        $vocabUriSpace = $this->vocab->getUriSpace();
962
963
        if (!in_array($vocabUriSpace, $context, true)) {
964
            if (!isset($context[$vocabPrefix])) {
965
                $context[$vocabPrefix] = $vocabUriSpace;
966
            } elseif ($context[$vocabPrefix] !== $vocabUriSpace) {
967
                $i = 2;
968
                while (isset($context[$vocabPrefix . $i]) && $context[$vocabPrefix . $i] !== $vocabUriSpace) {
969
                    $i += 1;
970
                }
971
                $context[$vocabPrefix . $i] = $vocabUriSpace;
972
            }
973
        }
974
        $compactJsonLD = \ML\JsonLD\JsonLD::compact($this->graph->serialise('jsonld'), json_encode($context));
975
        return \ML\JsonLD\JsonLD::toString($compactJsonLD);
976
    }
977
978
    public function isUseModifiedDate()
979
    {
980
        return $this->getVocab()->isUseModifiedDate();
981
    }
982
}
983