Completed
Branch master (8e2732)
by Osma
04:46 queued 01:55
created

RestController::negotiateFormat()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 15
rs 9.2
cc 4
eloc 9
nc 4
nop 3
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
 * RestController is responsible for handling all the requests directed to the /rest address.
10
 */
11
class RestController extends Controller
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...
12
{
13
    /* supported MIME types that can be used to return RDF data */
14
    const SUPPORTED_FORMATS = 'application/rdf+xml text/turtle application/ld+json application/json';
15
    /* context array template */
16
    private $context = array(
17
        '@context' => array(
18
            'skos' => 'http://www.w3.org/2004/02/skos/core#',
19
            'uri' => '@id',
20
            'type' => '@type',
21
        ),
22
    );
23
24
    /**
25
     * Echos an error message when the request can't be fulfilled.
26
     * @param string $code
27
     * @param string $status
28
     * @param string $message
29
     */
30
    private function returnError($code, $status, $message)
31
    {
32
        header("HTTP/1.0 $code $status");
33
        header("Content-type: text/plain; charset=utf-8");
34
        echo "$code $status : $message";
35
    }
36
37
    /**
38
     * Handles json encoding, adding the content type headers and optional callback function.
39
     * @param array $data the data to be returned.
40
     */
41
    private function returnJson($data)
42
    {
43
        // wrap with JSONP callback if requested
44
        if (filter_input(INPUT_GET, 'callback', FILTER_SANITIZE_STRING)) {
45
            header("Content-type: application/javascript; charset=utf-8");
46
            echo filter_input(INPUT_GET, 'callback', FILTER_UNSAFE_RAW) . "(" . json_encode($data) . ");";
47
            return;
48
        }
49
        
50
        // otherwise negotiate suitable format for the response and return that
51
        $negotiator = new \Negotiation\FormatNegotiator();
52
        $priorities = array('application/json', 'application/ld+json');
53
        $best = filter_input(INPUT_SERVER, 'HTTP_ACCEPT', FILTER_SANITIZE_STRING) ? $negotiator->getBest(filter_input(INPUT_SERVER, 'HTTP_ACCEPT', FILTER_SANITIZE_STRING), $priorities) : null;
54
        $format = ($best !== null) ? $best->getValue() : $priorities[0];
55
        header("Content-type: $format; charset=utf-8");
56
        header("Vary: Accept"); // inform caches that we made a choice based on Accept header
57
        echo json_encode($data);
58
    }
59
60
    /**
61
     * Parses and returns the limit parameter. Returns and error if the parameter is missing.
62
     */
63
    private function parseLimit()
64
    {
65
        $limit = filter_input(INPUT_GET, 'limit', FILTER_SANITIZE_NUMBER_INT) ? filter_input(INPUT_GET, 'limit', FILTER_SANITIZE_NUMBER_INT) : $this->model->getConfig()->getDefaultTransitiveLimit();
66
        if ($limit <= 0) {
67
            return $this->returnError(400, "Bad Request", "Invalid limit parameter");
68
        }
69
70
        return $limit;
71
    }
72
73
74
/** Global REST methods **/
75
76
    /**
77
     * Returns all the vocabularies available on the server in a json object.
78
     */
79
    public function vocabularies($request)
80
    {
81
        if (!$request->getLang()) {
82
            return $this->returnError(400, "Bad Request", "lang parameter missing");
83
        }
84
85
        $this->setLanguageProperties($request->getLang());
86
87
        $vocabs = array();
88
        foreach ($this->model->getVocabularies() as $voc) {
89
            $vocabs[$voc->getId()] = $voc->getConfig()->getTitle($request->getLang());
90
        }
91
        ksort($vocabs);
92
        $results = array();
93
        foreach ($vocabs as $id => $title) {
94
            $results[] = array(
95
                'uri' => $id,
96
                'id' => $id,
97
                'title' => $title);
98
        }
99
100
        /* encode the results in a JSON-LD compatible array */
101
        $ret = array(
102
            '@context' => array(
103
                'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
104
                'onki' => 'http://schema.onki.fi/onki#',
105
                'title' => array('@id' => 'rdfs:label', '@language' => $request->getLang()),
106
                'vocabularies' => 'onki:hasVocabulary',
107
                'id' => 'onki:vocabularyIdentifier',
108
                'uri' => '@id',
109
            ),
110
            'uri' => '',
111
            'vocabularies' => $results,
112
        );
113
114
        return $this->returnJson($ret);
115
    }
116
117
    /**
118
     * Performs the search function calls. And wraps the result in a json-ld object.
119
     * @param Request $request
120
     */
121
    public function search($request)
122
    {
123
        $maxhits = $request->getQueryParam('maxhits');
124
        $offset = $request->getQueryParam('offset');
125
        $term = $request->getQueryParam('query');
126
127
        if (!$term) {
128
            return $this->returnError(400, "Bad Request", "query parameter missing");
129
        }
130
        if ($maxhits && (!is_numeric($maxhits) || $maxhits <= 0)) {
131
            return $this->returnError(400, "Bad Request", "maxhits parameter is invalid");
132
        }
133
        if ($offset && (!is_numeric($offset) || $offset < 0)) {
134
            return $this->returnError(400, "Bad Request", "offset parameter is invalid");
135
        }
136
137
        $parameters = new ConceptSearchParameters($request, $this->model->getConfig(), true);
138
        
139
        $vocabs = $request->getQueryParam('vocab'); # optional
140
        // convert to vocids array to support multi-vocabulary search
141
        $vocids = ($vocabs !== null && $vocabs !== '') ? explode(' ', $vocabs) : array();
142
        $vocabObjects = array();
143
        foreach($vocids as $vocid) {
144
            $vocabObjects[] = $this->model->getVocabulary($vocid);
145
        }
146
        $parameters->setVocabularies($vocabObjects);
147
148
        $results = $this->model->searchConcepts($parameters);
149
        // before serializing to JSON, get rid of the Vocabulary object that came with each resource
150
        foreach ($results as &$res) {
151
            unset($res['voc']);
152
        }
153
154
        $ret = array(
155
            '@context' => array(
156
                'skos' => 'http://www.w3.org/2004/02/skos/core#',
157
                'onki' => 'http://schema.onki.fi/onki#',
158
                'uri' => '@id',
159
                'type' => '@type',
160
                'results' => array(
161
                    '@id' => 'onki:results',
162
                    '@container' => '@list',
163
                ),
164
                'prefLabel' => 'skos:prefLabel',
165
                'altLabel' => 'skos:altLabel',
166
                'hiddenLabel' => 'skos:hiddenLabel',
167
            ),
168
            'uri' => '',
169
            'results' => $results,
170
        );
171
        
172
        if (isset($results[0]['prefLabels'])) {
173
            $ret['@context']['prefLabels'] = array('@id' => 'skos:prefLabel', '@container' => '@language');
174
        }
175
176
        if ($request->getQueryParam('fields')) {
177
            $fields = explode(' ', $request->getQueryParam('fields'));
178
            foreach ($fields as $prop) {
179
                $ret['@context'][$prop] = "skos:{$prop}";
180
            }
181
        }
182
183
        if ($request->getQueryParam('labellang')) {
184
            $ret['@context']['@language'] = $request->getQueryParam('labellang');
185
        } elseif ($request->getQueryParam('lang')) {
186
            $ret['@context']['@language'] = $request->getQueryParam('lang');;
187
        }
188
189
        return $this->returnJson($ret);
190
    }
191
192
/** Vocabulary-specific methods **/
193
194
    /**
195
     * Loads the vocabulary metadata. And wraps the result in a json-ld object.
196
     * @param Request $request
197
     */
198
    public function vocabularyInformation($request)
199
    {
200
        $vocab = $request->getVocab();
201
202
        /* encode the results in a JSON-LD compatible array */
203
        $conceptschemes = array();
204
        foreach ($vocab->getConceptSchemes($request->getLang()) as $uri => $csdata) {
205
            $csdata['uri'] = $uri;
206
            $csdata['type'] = 'skos:ConceptScheme';
207
            $conceptschemes[] = $csdata;
208
        }
209
210
        $ret = array(
211
            '@context' => array(
212
                'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
213
                'skos' => 'http://www.w3.org/2004/02/skos/core#',
214
                'onki' => 'http://schema.onki.fi/onki#',
215
                'dct' => 'http://purl.org/dc/terms/',
216
                'uri' => '@id',
217
                'type' => '@type',
218
                'title' => 'rdfs:label',
219
                'conceptschemes' => 'onki:hasConceptScheme',
220
                'id' => 'onki:vocabularyIdentifier',
221
                'defaultLanguage' => 'onki:defaultLanguage',
222
                'languages' => 'onki:language',
223
                'label' => 'rdfs:label',
224
                'prefLabel' => 'skos:prefLabel',
225
                'title' => 'dct:title',
226
                '@language' => $request->getLang(),
227
            ),
228
            'uri' => '',
229
            'id' => $vocab->getId(),
230
            'title' => $vocab->getConfig()->getTitle($request->getLang()),
231
            'defaultLanguage' => $vocab->getConfig()->getDefaultLanguage(),
232
            'languages' => $vocab->getConfig()->getLanguages(),
233
            'conceptschemes' => $conceptschemes,
234
        );
235
236
        return $this->returnJson($ret);
237
    }
238
239
    /**
240
     * Loads the vocabulary metadata. And wraps the result in a json-ld object.
241
     * @param Request $request
242
     */
243
    public function vocabularyStatistics($request)
244
    {
245
        $this->setLanguageProperties($request->getLang());
246
        $arrayClass = $request->getVocab()->getConfig()->getArrayClassURI(); 
247
        $groupClass = $request->getVocab()->getConfig()->getGroupClassURI(); 
248
        $vocabStats = $request->getVocab()->getStatistics($request->getQueryParam('lang'), $arrayClass, $groupClass);
249
        $types = array('http://www.w3.org/2004/02/skos/core#Concept', 'http://www.w3.org/2004/02/skos/core#Collection', $arrayClass, $groupClass);
250
        $subTypes = array();
251
        foreach ($vocabStats as $subtype) {
252
            if (!in_array($subtype['type'], $types)) {
253
                $subTypes[] = $subtype;
254
            }
255
        }
256
257
        /* encode the results in a JSON-LD compatible array */
258
        $ret = array(
259
            '@context' => array(
260
                'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
261
                'skos' => 'http://www.w3.org/2004/02/skos/core#',
262
                'void' => 'http://rdfs.org/ns/void#',
263
                'onki' => 'http://schema.onki.fi/onki#',
264
                'uri' => '@id',
265
                'id' => 'onki:vocabularyIdentifier',
266
                'concepts' => 'void:classPartition',
267
                'label' => 'rdfs:label',
268
                'class' => array('@id' => 'void:class', '@type' => '@id'),
269
                'subTypes' => array('@id' => 'void:class', '@type' => '@id'),
270
                'count' => 'void:entities',
271
                '@language' => $request->getLang(),
272
            ),
273
            'uri' => '',
274
            'id' => $request->getVocab()->getId(),
275
            'title' => $request->getVocab()->getConfig()->getTitle(),
276
            'concepts' => array(
277
                'class' => 'http://www.w3.org/2004/02/skos/core#Concept',
278
                'label' => gettext('skos:Concept'),
279
                'count' => $vocabStats['http://www.w3.org/2004/02/skos/core#Concept']['count'],
280
            ),
281
            'subTypes' => $subTypes,
282
        );
283
284
        if (isset($vocabStats['http://www.w3.org/2004/02/skos/core#Collection'])) {
285
            $ret['conceptGroups'] = array(
286
                'class' => 'http://www.w3.org/2004/02/skos/core#Collection',
287
                'label' => gettext('skos:Collection'),
288
                'count' => $vocabStats['http://www.w3.org/2004/02/skos/core#Collection']['count'],
289
            );
290
        } else if (isset($vocabStats[$groupClass])) {
291
            $ret['conceptGroups'] = array(
292
                'class' => $groupClass,
293
                'label' => isset($vocabStats[$groupClass]['label']) ? $vocabStats[$groupClass]['label'] : gettext(EasyRdf_Namespace::shorten($groupClass)),
294
                'count' => $vocabStats[$groupClass]['count'],
295
            );
296
        } else if (isset($vocabStats[$arrayClass])) {
297
            $ret['arrays'] = array(
298
                'class' => $arrayClass,
299
                'label' => isset($vocabStats[$arrayClass]['label']) ? $vocabStats[$arrayClass]['label'] : gettext(EasyRdf_Namespace::shorten($arrayClass)),
300
                'count' => $vocabStats[$arrayClass]['count'],
301
            );
302
        }
303
304
        return $this->returnJson($ret);
305
    }
306
307
    /**
308
     * Loads the vocabulary metadata. And wraps the result in a json-ld object.
309
     * @param Request $request
310
     */
311
    public function labelStatistics($request)
312
    {
313
        $lang = $request->getLang();
314
        $this->setLanguageProperties($request->getLang());
315
        $vocabStats = $request->getVocab()->getLabelStatistics();
316
317
        /* encode the results in a JSON-LD compatible array */
318
        $counts = array();
319
        foreach ($vocabStats['terms'] as $proplang => $properties) {
320
            $langdata = array('language' => $proplang);
321
            if ($lang) {
322
                $langdata['literal'] = Punic\Language::getName($proplang, $lang);
323
            }
324
325
            $langdata['properties'] = array();
326
            foreach ($properties as $prop => $value) {
327
                $langdata['properties'][] = array('property' => $prop, 'labels' => $value);
328
            }
329
            $counts[] = $langdata;
330
        }
331
332
        $ret = array(
333
            '@context' => array(
334
                'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
335
                'skos' => 'http://www.w3.org/2004/02/skos/core#',
336
                'void' => 'http://rdfs.org/ns/void#',
337
                'void-ext' => 'http://ldf.fi/void-ext#',
338
                'onki' => 'http://schema.onki.fi/onki#',
339
                'uri' => '@id',
340
                'id' => 'onki:vocabularyIdentifier',
341
                'languages' => 'void-ext:languagePartition',
342
                'language' => 'void-ext:language',
343
                'properties' => 'void:propertyPartition',
344
                'labels' => 'void:triples',
345
            ),
346
            'uri' => '',
347
            'id' => $request->getVocab()->getId(),
348
            'title' => $request->getVocab()->getConfig()->getTitle($lang),
349
            'languages' => $counts,
350
        );
351
352
        if ($lang) {
353
            $ret['@context']['literal'] = array('@id' => 'rdfs:label', '@language' => $lang);
354
        }
355
356
        return $this->returnJson($ret);
357
    }
358
359
    /**
360
     * Loads the vocabulary type metadata. And wraps the result in a json-ld object.
361
     * @param Request $request
362
     */
363
    public function types($request)
364
    {
365
        $vocid = $request->getVocab() ? $request->getVocab()->getId() : null;
366
        if ($vocid === null && !$request->getLang()) {
367
            return $this->returnError(400, "Bad Request", "lang parameter missing");
368
        }
369
        $this->setLanguageProperties($request->getLang());
370
        
371
        $queriedtypes = $this->model->getTypes($vocid, $request->getLang());
372
373
        $types = array();
374
375
        /* encode the results in a JSON-LD compatible array */
376
        foreach ($queriedtypes as $uri => $typedata) {
377
            $type = array_merge(array('uri' => $uri), $typedata);
378
            $types[] = $type;
379
        }
380
381
        $ret = array_merge_recursive($this->context, array(
382
            '@context' => array('rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', 'onki' => 'http://schema.onki.fi/onki#', 'label' => 'rdfs:label', 'superclass' => array('@id' => 'rdfs:subClassOf', '@type' => '@id'), 'types' => 'onki:hasType', '@language' => $request->getLang()),
383
            'uri' => '',
384
            'types' => $types)
385
        );
386
387
        return $this->returnJson($ret);
388
    }
389
390
    /**
391
     * Used for finding terms by their exact prefLabel. Wraps the result in a json-ld object.
392
     * @param Request $request
393
     */
394
    public function lookup($request)
395
    {
396
        $label = $request->getQueryParam('label');
397
        if (!$label) {
398
            return $this->returnError(400, "Bad Request", "label parameter missing");
399
        }
400
401
        $lang = $request->getQueryParam('lang');
402
403
        $parameters = new ConceptSearchParameters($request, $this->model->getConfig(), true);
404
405
        $results = $this->model->searchConcepts($parameters);
406
407
        $hits = array();
408
        // case 1: exact match on preferred label
409
        foreach ($results as $res) {
410
            if ($res['prefLabel'] == $label) {
411
                $hits[] = $res;
412
            }
413
        }
414
415
        // case 2: case-insensitive match on preferred label
416
        if (sizeof($hits) == 0) { // not yet found
417
            foreach ($results as $res) {
418
                if (strtolower($res['prefLabel']) == strtolower($label)) {
419
                    $hits[] = $res;
420
                }
421
            }
422
423
        }
424
425
        // case 3: exact match on alternate label
426 View Code Duplication
        if (sizeof($hits) == 0) { // not yet found
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...
427
            foreach ($results as $res) {
428
                if (isset($res['altLabel']) && $res['altLabel'] == $label) {
429
                    $hits[] = $res;
430
                }
431
            }
432
433
        }
434
435
        // case 4: case-insensitive match on alternate label
436 View Code Duplication
        if (sizeof($hits) == 0) { // not yet found
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...
437
            foreach ($results as $res) {
438
                if (isset($res['altLabel']) && strtolower($res['altLabel']) == strtolower($label)) {
439
                    $hits[] = $res;
440
                }
441
            }
442
443
        }
444
445
        if (sizeof($hits) == 0) {
446
            // no matches found
447
            return $this->returnError(404, 'Not Found', "Could not find label '$label'");
448
        }
449
450
        // did find some matches!
451
        // get rid of Vocabulary objects
452
        foreach ($hits as &$res) {
453
            unset($res['voc']);
454
        }
455
456
        $ret = array_merge_recursive($this->context, array(
457
            '@context' => array('onki' => 'http://schema.onki.fi/onki#', 'results' => array('@id' => 'onki:results'), 'prefLabel' => 'skos:prefLabel', 'altLabel' => 'skos:altLabel', 'hiddenLabel' => 'skos:hiddenLabel'),
458
            'result' => $hits)
459
        );
460
461
        if ($lang) {
462
            $ret['@context']['@language'] = $lang;
463
        }
464
465
        return $this->returnJson($ret);
466
    }
467
468
    /**
469
     * Queries the top concepts of a vocabulary and wraps the results in a json-ld object.
470
     * @param Request $request
471
     * @return object json-ld object
472
     */
473
    public function topConcepts($request)
474
    {
475
        $vocab = $request->getVocab();
476
        $scheme = $request->getQueryParam('scheme');
477
        if (!$scheme) {
478
            $scheme = $vocab->getConfig()->showConceptSchemesInHierarchy() ? array_keys($vocab->getConceptSchemes()) : $vocab->getDefaultConceptScheme();
479
        }
480
481
        /* encode the results in a JSON-LD compatible array */
482
        $topconcepts = $vocab->getTopConcepts($scheme, $request->getLang());
483
484
        $ret = array_merge_recursive($this->context, array(
485
            '@context' => array('onki' => 'http://schema.onki.fi/onki#', 'topconcepts' => 'skos:hasTopConcept', 'notation' => 'skos:notation', 'label' => 'skos:prefLabel', '@language' => $request->getLang()),
486
            'uri' => $scheme,
487
            'topconcepts' => $topconcepts)
488
        );
489
490
        return $this->returnJson($ret);
491
    }
492
493
    /**
494
     * Download a concept as json-ld or redirect to download the whole vocabulary.
495
     * @param Request $request
496
     * @return object json-ld formatted concept.
497
     */
498
    public function data($request)
499
    {
500
        $vocab = $request->getVocab();
501
        $format = $request->getQueryParam('format');
502
503
        if ($request->getUri()) {
504
            $uri = $request->getUri();
505
        } else if ($vocab !== null) { // whole vocabulary - redirect to download URL
506
            $urls = $vocab->getConfig()->getDataURLs();
507
            if (sizeof($urls) == 0) {
508
                return $this->returnError('404', 'Not Found', "No download source URL known for vocabulary $vocab");
509
            }
510
511
            $format = $this->negotiateFormat(array_keys($urls), $request->getServerConstant('HTTP_ACCEPT'), $format);
512
            if (!$format) {
513
                return $this->returnError(406, 'Not Acceptable', "Unsupported format. Supported MIME types are: " . implode(' ', array_keys($urls)));
514
            }
515
516
            header("Location: " . $urls[$format]);
517
            return;
518
        }
519
520
        $format = $this->negotiateFormat(explode(' ', self::SUPPORTED_FORMATS), $request->getServerConstant('HTTP_ACCEPT'), $format);
521
        if (!$format) {
522
            return $this->returnError(406, 'Not Acceptable', "Unsupported format. Supported MIME types are: " . self::SUPPORTED_FORMATS);
523
        }
524
        if (!isset($uri) && !isset($urls)) {
0 ignored issues
show
Bug introduced by
The variable $urls seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
525
            return $this->returnError(400, 'Bad Request', "uri parameter missing");
526
        }
527
528
        $vocid = $vocab ? $vocab->getId() : null;
529
        $results = $this->model->getRDF($vocid, $uri, $format);
530
531
        if ($format == 'application/ld+json' || $format == 'application/json') {
532
            // further compact JSON-LD document using a context
533
            $context = array(
534
                'skos' => 'http://www.w3.org/2004/02/skos/core#',
535
                'isothes' => 'http://purl.org/iso25964/skos-thes#',
536
                'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
537
                'owl' => 'http://www.w3.org/2002/07/owl#',
538
                'dct' => 'http://purl.org/dc/terms/',
539
                'dc11' => 'http://purl.org/dc/elements/1.1/',
540
                'uri' => '@id',
541
                'type' => '@type',
542
                'lang' => '@language',
543
                'value' => '@value',
544
                'graph' => '@graph',
545
                'label' => 'rdfs:label',
546
                'prefLabel' => 'skos:prefLabel',
547
                'altLabel' => 'skos:altLabel',
548
                'hiddenLabel' => 'skos:hiddenLabel',
549
                'broader' => 'skos:broader',
550
                'narrower' => 'skos:narrower',
551
                'related' => 'skos:related',
552
                'inScheme' => 'skos:inScheme',
553
            );
554
            $compactJsonLD = \ML\JsonLD\JsonLD::compact($results, json_encode($context));
555
            $results = \ML\JsonLD\JsonLD::toString($compactJsonLD);
556
        }
557
558
        header("Content-type: $format; charset=utf-8");
559
        echo $results;
560
    }
561
562
    /**
563
     * Used for querying labels for a uri.
564
     * @param Request $request
565
     * @return object json-ld wrapped labels.
566
     */
567
    public function label($request)
568
    {
569
        if (!$request->getUri()) {
570
            return $this->returnError(400, "Bad Request", "uri parameter missing");
571
        }
572
573
        $results = $request->getVocab()->getConceptLabel($request->getUri(), $request->getLang());
574
        if ($results === null) {
575
            return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>");
576
        }
577
578
        $ret = array_merge_recursive($this->context, array(
579
            '@context' => array('prefLabel' => 'skos:prefLabel', '@language' => $request->getLang()),
580
            'uri' => $request->getUri())
581
        );
582
583
        if (isset($results[$request->getLang()])) {
584
            $ret['prefLabel'] = $results[$request->getLang()]->getValue();
585
        }
586
587
        return $this->returnJson($ret);
588
    }
589
590
    /**
591
     * Used for querying broader relations for a concept.
592
     * @param Request $request
593
     * @return object json-ld wrapped broader concept uris and labels.
594
     */
595 View Code Duplication
    public function broader($request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
596
    {
597
        $results = array();
598
        $broaders = $request->getVocab()->getConceptBroaders($request->getUri(), $request->getLang());
599
        if ($broaders === null) {
600
            return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>");
601
        }
602
603
        foreach ($broaders as $object => $vals) {
604
            $results[] = array('uri' => $object, 'prefLabel' => $vals['label']);
605
        }
606
607
        $ret = array_merge_recursive($this->context, array(
608
            '@context' => array('prefLabel' => 'skos:prefLabel', 'broader' => 'skos:broader', '@language' => $request->getLang()),
609
            'uri' => $request->getUri(),
610
            'broader' => $results)
611
        );
612
613
        return $this->returnJson($ret);
614
    }
615
616
    /**
617
     * Used for querying broader transitive relations for a concept.
618
     * @param Request $request
619
     * @return object json-ld wrapped broader transitive concept uris and labels.
620
     */
621 View Code Duplication
    public function broaderTransitive($request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
622
    {
623
        $results = array();
624
        $broaders = $request->getVocab()->getConceptTransitiveBroaders($request->getUri(), $this->parseLimit(), false, $request->getLang());
625
        if (empty($broaders)) {
626
            return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>");
627
        }
628
629
        foreach ($broaders as $buri => $vals) {
630
            $result = array('uri' => $buri, 'prefLabel' => $vals['label']);
631
            if (isset($vals['direct'])) {
632
                $result['broader'] = $vals['direct'];
633
            }
634
            $results[$buri] = $result;
635
        }
636
637
        $ret = array_merge_recursive($this->context, array(
638
            '@context' => array('prefLabel' => 'skos:prefLabel', 'broader' => array('@id' => 'skos:broader', '@type' => '@id'), 'broaderTransitive' => array('@id' => 'skos:broaderTransitive', '@container' => '@index'), '@language' => $request->getLang()),
639
            'uri' => $request->getUri(),
640
            'broaderTransitive' => $results)
641
        );
642
643
        return $this->returnJson($ret);
644
    }
645
646
    /**
647
     * Used for querying narrower relations for a concept.
648
     * @param Request $request
649
     * @return object json-ld wrapped narrower concept uris and labels.
650
     */
651 View Code Duplication
    public function narrower($request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
652
    {
653
        $results = array();
654
        $narrowers = $request->getVocab()->getConceptNarrowers($request->getUri(), $request->getLang());
655
        if ($narrowers === null) {
656
            return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>");
657
        }
658
659
        foreach ($narrowers as $object => $vals) {
660
            $results[] = array('uri' => $object, 'prefLabel' => $vals['label']);
661
        }
662
663
        $ret = array_merge_recursive($this->context, array(
664
            '@context' => array('prefLabel' => 'skos:prefLabel', 'narrower' => 'skos:narrower', '@language' => $request->getLang()),
665
            'uri' => $request->getUri(),
666
            'narrower' => $results)
667
        );
668
669
        return $this->returnJson($ret);
670
    }
671
672
    /**
673
     * Used for querying narrower transitive relations for a concept.
674
     * @param Request $request
675
     * @return object json-ld wrapped narrower transitive concept uris and labels.
676
     */
677 View Code Duplication
    public function narrowerTransitive($request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
678
    {
679
        $results = array();
680
        $narrowers = $request->getVocab()->getConceptTransitiveNarrowers($request->getUri(), $this->parseLimit(), $request->getLang());
681
        if (empty($narrowers)) {
682
            return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>");
683
        }
684
685
        foreach ($narrowers as $nuri => $vals) {
686
            $result = array('uri' => $nuri, 'prefLabel' => $vals['label']);
687
            if (isset($vals['direct'])) {
688
                $result['narrower'] = $vals['direct'];
689
            }
690
            $results[$nuri] = $result;
691
        }
692
693
        $ret = array_merge_recursive($this->context, array(
694
            '@context' => array('prefLabel' => 'skos:prefLabel', 'narrower' => array('@id' => 'skos:narrower', '@type' => '@id'), 'narrowerTransitive' => array('@id' => 'skos:narrowerTransitive', '@container' => '@index'), '@language' => $request->getLang()),
695
            'uri' => $request->getUri(),
696
            'narrowerTransitive' => $results)
697
        );
698
699
        return $this->returnJson($ret);
700
    }
701
702
    /**
703
     * Used for querying broader transitive relations
704
     * and some narrowers for a concept in the hierarchy view.
705
     * @param Request $request
706
     * @return object json-ld wrapped hierarchical concept uris and labels.
707
     */
708
    public function hierarchy($request)
709
    {
710
        $results = $request->getVocab()->getConceptHierarchy($request->getUri(), $request->getLang());
711
        if (empty($results)) {
712
            return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>");
713
        }
714
715
        if ($request->getVocab()->getConfig()->getShowHierarchy()) {
716
            $schemes = $request->getVocab()->getConceptSchemes($request->getLang());
717
            foreach ($schemes as $scheme) {
718
                if (!isset($scheme['title']) && !isset($scheme['label']) && !isset($scheme['prefLabel'])) {
719
                    unset($schemes[array_search($scheme, $schemes)]);
720
                }
721
722
            }
723
724
            /* encode the results in a JSON-LD compatible array */
725
            $topconcepts = $request->getVocab()->getTopConcepts(array_keys($schemes), $request->getLang());
726
            foreach ($topconcepts as $top) {
727
                if (!isset($results[$top['uri']])) {
728
                    $results[$top['uri']] = array('uri' => $top['uri'], 'top' => $top['topConceptOf'], 'prefLabel' => $top['label'], 'hasChildren' => $top['hasChildren']);
729
                    if (isset($top['notation'])) {
730
                        $results[$top['uri']]['notation'] = $top['notation'];
731
                    }
732
733
                }
734
            }
735
        }
736
737
        $ret = array_merge_recursive($this->context, array(
738
            '@context' => array('onki' => 'http://schema.onki.fi/onki#', 'prefLabel' => 'skos:prefLabel', 'notation' => 'skos:notation', 'narrower' => array('@id' => 'skos:narrower', '@type' => '@id'), 'broader' => array('@id' => 'skos:broader', '@type' => '@id'), 'broaderTransitive' => array('@id' => 'skos:broaderTransitive', '@container' => '@index'), 'top' => array('@id' => 'skos:topConceptOf', '@type' => '@id'), 'hasChildren' => 'onki:hasChildren', '@language' => $request->getLang()),
739
            'uri' => $request->getUri(),
740
            'broaderTransitive' => $results)
741
        );
742
743
        return $this->returnJson($ret);
744
    }
745
746
    /**
747
     * Used for querying group hierarchy for the sidebar group view.
748
     * @param Request $request
749
     * @return object json-ld wrapped hierarchical concept uris and labels.
750
     */
751
    public function groups($request)
752
    {
753
        $results = $request->getVocab()->listConceptGroups($request->getLang());
754
755
        $ret = array_merge_recursive($this->context, array(
756
            '@context' => array('onki' => 'http://schema.onki.fi/onki#', 'prefLabel' => 'skos:prefLabel', 'groups' => 'onki:hasGroup', 'childGroups' => array('@id' => 'skos:member', '@type' => '@id'), 'hasMembers' => 'onki:hasMembers', '@language' => $request->getLang()),
757
            'uri' => '',
758
            'groups' => $results)
759
        );
760
761
        return $this->returnJson($ret);
762
    }
763
764
    /**
765
     * Used for querying member relations for a group.
766
     * @param Request $request
767
     * @return object json-ld wrapped narrower concept uris and labels.
768
     */
769
    public function groupMembers($request)
770
    {
771
        $children = $request->getVocab()->listConceptGroupContents($request->getUri(), $request->getLang());
772
        if (empty($children)) {
773
            return $this->returnError('404', 'Not Found', "Could not find group <{$request->getUri()}>");
774
        }
775
776
        $ret = array_merge_recursive($this->context, array(
777
            '@context' => array('prefLabel' => 'skos:prefLabel', 'members' => 'skos:member', '@language' => $request->getLang()),
778
            'uri' => $request->getUri(),
779
            'members' => $children)
780
        );
781
782
        return $this->returnJson($ret);
783
    }
784
785
    /**
786
     * Used for querying narrower relations for a concept in the hierarchy view.
787
     * @param Request $request
788
     * @return object json-ld wrapped narrower concept uris and labels.
789
     */
790
    public function children($request)
791
    {
792
        $children = $request->getVocab()->getConceptChildren($request->getUri(), $request->getLang());
793
        if ($children === null) {
794
            return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>");
795
        }
796
797
        $ret = array_merge_recursive($this->context, array(
798
            '@context' => array('prefLabel' => 'skos:prefLabel', 'narrower' => 'skos:narrower', 'notation' => 'skos:notation', 'hasChildren' => 'onki:hasChildren', '@language' => $request->getLang()),
799
            'uri' => $request->getUri(),
800
            'narrower' => $children)
801
        );
802
803
        return $this->returnJson($ret);
804
    }
805
806
    /**
807
     * Used for querying narrower relations for a concept in the hierarchy view.
808
     * @param Request $request
809
     * @return object json-ld wrapped hierarchical concept uris and labels.
810
     */
811
    public function related($request)
812
    {
813
        $results = array();
814
        $related = $request->getVocab()->getConceptRelateds($request->getUri(), $request->getLang());
815
        if ($related === null) {
816
            return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>");
817
        }
818
819
        foreach ($related as $uri => $vals) {
820
            $results[] = array('uri' => $uri, 'prefLabel' => $vals['label']);
821
        }
822
823
        $ret = array_merge_recursive($this->context, array(
824
            '@context' => array('prefLabel' => 'skos:prefLabel', 'related' => 'skos:related', '@language' => $request->getLang()),
825
            'uri' => $request->getUri(),
826
            'related' => $results)
827
        );
828
829
        return $this->returnJson($ret);
830
    }
831
}
832