Completed
Push — master ( d08d78...501888 )
by Henri
02:49
created

Concept::setFoundBy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
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('dc11:created')) {
437
                $created = $this->resource->get('dc11:created')->getValue();
438
            } else if ($this->resource->get('dc:created')) {
439
                $created = $this->resource->get('dc:created')->getValue();
440
            }
441
442
            // finding the modified properties
443
            if ($this->resource->get('dc11:modified')) {
444
                $modified = $this->resource->get('dc11:modified')->getValue();
445
            } else if ($this->resource->get('dc:modified')) {
446
                $modified = $this->resource->get('dc:modified')->getValue();
447
            }
448
449
            // making a human readable string from the timestamps
450
            if ($created != '') {
451
                $ret = gettext('skosmos:created') . ' ' . (Punic\Calendar::formatDate($created, 'short'));
452
            }
453
454
            if ($modified != '') {
455
                if ($created != '') {
456
                    $ret .= ', ' . gettext('skosmos:modified') . ' ' . (Punic\Calendar::formatDate($modified, 'short'));
457
                } else {
458
                    $ret .= ' ' . ucfirst(gettext('skosmos:modified')) . ' ' . (Punic\Calendar::formatDate($modified, 'short'));
459
                }
460
461
            }
462
        } catch (Exception $e) {
463
            trigger_error($e->getMessage(), E_USER_WARNING);
464
            return gettext('skosmos:modified') . ' ' . (string) $this->resource->get('dc:modified') . ' ' . gettext('skosmos:created') . ' ' . (string) $this->resource->get('dc:created');
465
        }
466
        return $ret;
467
    }
468
469
    /**
470
     * Gets the members of a specific collection.
471
     * @param $coll
472
     * @param array containing all narrowers as EasyRdf_Resource
473
     * @return array containing ConceptPropertyValue objects
474
     */
475
    private function getCollectionMembers($coll, $narrowers)
476
    {
477
        $members_array = array();
478
        $coll_label = $coll->label()->getValue($this->clang) ? $coll->label($this->clang) : $coll->label();
479
        if ($coll_label) {
480
            $coll_label = $coll_label->getValue();
481
        }
482
483
        $members_array[$coll_label] = new ConceptPropertyValue($this->model, $this->vocab, $coll, 'skos:narrower', $this->clang);
484
        foreach ($coll->allResources('skos:member') as $member) {
485
            if (array_key_exists($member->getUri(), $narrowers)) {
486
                $narrower = $narrowers[$member->getUri()];
487 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...
488
                    $members_array[$coll_label]->addSubMember(new ConceptPropertyValue($this->model, $this->vocab, $narrower, 'skos:member', $this->clang), $this->clang);
489
                }
490
491
            }
492
        }
493
494
        return $members_array;
495
    }
496
497
    /**
498
     * Gets the groups the concept belongs to.
499
     */
500
    public function getGroupProperties()
501
    {
502
        return $this->getReverseResources(false);
503
    }
504
505
    /**
506
     * Gets the groups/arrays the concept belongs to.
507
     */
508
    public function getReverseResources($includeArrays) {
509
        $groups = array();
510
        $reverseResources = $this->graph->resourcesMatching('skos:member', $this->resource);
511
        if (isset($reverseResources)) {
512
            $arrayClassURI = $this->vocab !== null ? $this->vocab->getConfig()->getArrayClassURI() : null;
513
            $arrayClass = $arrayClassURI !== null ? EasyRdf_Namespace::shorten($arrayClassURI) : null;
514
            foreach ($reverseResources as $reverseResource) {
515
                if (in_array($arrayClass, $reverseResource->types()) === $includeArrays) {
516
                    $property = in_array($arrayClass, $reverseResource->types()) ? "skosmos:memberOfArray" : "skosmos:memberOf";
517
                    $coll_label = $reverseResource->label($this->clang) ? $reverseResource->label($this->clang) : $reverseResource->label();
518
                    if ($coll_label) {
519
                        $coll_label = $coll_label->getValue();
520
                    }
521
522
                    $groups[$coll_label] = new ConceptPropertyValue($this->model, $this->vocab, $reverseResource, $property, $this->clang);
523
                    ksort($groups);
524
                    $super = $this->graph->resourcesMatching('skos:member', $reverseResource);
525
                    while (isset($super) && !empty($super)) {
526
                        foreach ($super as $res) {
527
                            $superprop = new ConceptPropertyValue($this->model, $this->vocab, $res, 'skosmos:memberOfSuper', $this->clang);
528
                            array_unshift($groups, $superprop);
529
                            $super = $this->graph->resourcesMatching('skos:member', $res);
530
                        }
531
                    }
532
                }
533
            }
534
        }
535
        return $groups;
536
    }
537
538
    public function getArrayProperties() {
539
        return $this->getReverseResources(true);
540
    }
541
542
    /**
543
     * Gets the values for the property in question in all other languages than the ui language.
544
     */
545
    public function getForeignLabels()
546
    {
547
        $labels = array();
548 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...
549
            // filtering away subsets of the current language eg. en vs en-GB
550
            if ($lit->getLang() != $this->clang && strpos($lit->getLang(), $this->getEnvLang() . '-') !== 0) {
551
                $labels[Punic\Language::getName($lit->getLang(), $this->getEnvLang())][] = new ConceptPropertyValueLiteral($lit, 'skos:prefLabel');
552
            }
553
554
        }
555 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...
556
            // filtering away subsets of the current language eg. en vs en-GB
557
            if ($lit->getLang() != $this->clang && strpos($lit->getLang(), $this->getEnvLang() . '-') !== 0) {
558
                $labels[Punic\Language::getName($lit->getLang(), $this->getEnvLang())][] = new ConceptPropertyValueLiteral($lit, 'skos:altLabel');
559
            }
560
561
        }
562
        ksort($labels);
563
        return $labels;
564
    }
565
566
    /**
567
     * Gets the values for the property in question in all other languages than the ui language.
568
     * @param string $property
569
     */
570
    public function getAllLabels($property)
571
    {
572
        $labels = array();
573
        if (EasyRdf_Namespace::shorten($property) !== null) {
574
            // shortening property labels if possible
575
            $property = EasyRdf_Namespace::shorten($property);
576
        } else {
577
            $property = "<$property>";
578
        }
579
        // EasyRdf requires full URIs to be in angle brackets
580
        foreach ($this->resource->allLiterals($property) as $lit) {
581
            $labels[Punic\Language::getName($lit->getLang(), $this->getEnvLang())][] = new ConceptPropertyValueLiteral($lit, $property);
582
        }
583
        ksort($labels);
584
        return $labels;
585
    }
586
}
587