Total Complexity | 198 |
Total Lines | 1168 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like RestController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use RestController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
6 | class RestController extends Controller |
||
7 | { |
||
8 | /* supported MIME types that can be used to return RDF data */ |
||
9 | public const SUPPORTED_FORMATS = 'application/rdf+xml text/turtle application/ld+json application/json application/marcxml+xml'; |
||
10 | /* context array template */ |
||
11 | private $context = array( |
||
12 | '@context' => array( |
||
13 | 'skos' => 'http://www.w3.org/2004/02/skos/core#', |
||
14 | 'uri' => '@id', |
||
15 | 'type' => '@type', |
||
16 | ), |
||
17 | ); |
||
18 | |||
19 | /** |
||
20 | * Handles json encoding, adding the content type headers and optional callback function. |
||
21 | * @param array $data the data to be returned. |
||
22 | */ |
||
23 | private function returnJson($data) |
||
24 | { |
||
25 | // wrap with JSONP callback if requested |
||
26 | if (filter_input(INPUT_GET, 'callback', FILTER_SANITIZE_FULL_SPECIAL_CHARS)) { |
||
|
|||
27 | header("Content-type: application/javascript; charset=utf-8"); |
||
28 | echo filter_input(INPUT_GET, 'callback', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES) . "(" . json_encode($data) . ");"; |
||
29 | return; |
||
30 | } |
||
31 | |||
32 | // otherwise negotiate suitable format for the response and return that |
||
33 | $negotiator = new \Negotiation\Negotiator(); |
||
34 | $priorities = array('application/json', 'application/ld+json'); |
||
35 | $best = filter_input(INPUT_SERVER, 'HTTP_ACCEPT', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ? $negotiator->getBest(filter_input(INPUT_SERVER, 'HTTP_ACCEPT', FILTER_SANITIZE_FULL_SPECIAL_CHARS), $priorities) : null; |
||
36 | $format = ($best !== null) ? $best->getValue() : $priorities[0]; |
||
37 | header("Content-type: $format; charset=utf-8"); |
||
38 | header("Vary: Accept"); // inform caches that we made a choice based on Accept header |
||
39 | echo json_encode($data); |
||
40 | } |
||
41 | |||
42 | /** |
||
43 | * Parses and returns the limit parameter. Returns and error if the parameter is missing. |
||
44 | */ |
||
45 | private function parseLimit() |
||
46 | { |
||
47 | $limit = filter_input(INPUT_GET, 'limit', FILTER_SANITIZE_NUMBER_INT) ? filter_input(INPUT_GET, 'limit', FILTER_SANITIZE_NUMBER_INT) : $this->model->getConfig()->getDefaultTransitiveLimit(); |
||
48 | if ($limit <= 0) { |
||
49 | return $this->returnError(400, "Bad Request", "Invalid limit parameter"); |
||
50 | } |
||
51 | |||
52 | return $limit; |
||
53 | } |
||
54 | |||
55 | |||
56 | /** Global REST methods **/ |
||
57 | |||
58 | /** |
||
59 | * Returns all the vocabularies available on the server in a json object. |
||
60 | */ |
||
61 | public function vocabularies($request) |
||
62 | { |
||
63 | if (!$request->getLang()) { |
||
64 | return $this->returnError(400, "Bad Request", "lang parameter missing"); |
||
65 | } |
||
66 | |||
67 | $this->model->setLocale($request->getLang()); |
||
68 | |||
69 | $vocabs = array(); |
||
70 | foreach ($this->model->getVocabularies() as $voc) { |
||
71 | $vocabs[$voc->getId()] = $voc->getConfig()->getTitle($request->getLang()); |
||
72 | } |
||
73 | ksort($vocabs); |
||
74 | $results = array(); |
||
75 | foreach ($vocabs as $id => $title) { |
||
76 | $results[] = array( |
||
77 | 'uri' => $id, |
||
78 | 'id' => $id, |
||
79 | 'title' => $title); |
||
80 | } |
||
81 | |||
82 | /* encode the results in a JSON-LD compatible array */ |
||
83 | $ret = array( |
||
84 | '@context' => array( |
||
85 | 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', |
||
86 | 'onki' => 'http://schema.onki.fi/onki#', |
||
87 | 'title' => array('@id' => 'rdfs:label', '@language' => $request->getLang()), |
||
88 | 'vocabularies' => 'onki:hasVocabulary', |
||
89 | 'id' => 'onki:vocabularyIdentifier', |
||
90 | 'uri' => '@id', |
||
91 | '@base' => $this->getBaseHref() . "rest/v1/vocabularies", |
||
92 | ), |
||
93 | 'uri' => '', |
||
94 | 'vocabularies' => $results, |
||
95 | ); |
||
96 | |||
97 | return $this->returnJson($ret); |
||
98 | } |
||
99 | |||
100 | private function constructSearchParameters($request) |
||
101 | { |
||
102 | $parameters = new ConceptSearchParameters($request, $this->model->getConfig(), true); |
||
103 | |||
104 | $vocabs = $request->getQueryParam('vocab'); # optional |
||
105 | // convert to vocids array to support multi-vocabulary search |
||
106 | $vocids = ($vocabs !== null && $vocabs !== '') ? explode(' ', $vocabs) : array(); |
||
107 | $vocabObjects = array(); |
||
108 | foreach ($vocids as $vocid) { |
||
109 | $vocabObjects[] = $this->model->getVocabulary($vocid); |
||
110 | } |
||
111 | $parameters->setVocabularies($vocabObjects); |
||
112 | return $parameters; |
||
113 | } |
||
114 | |||
115 | private function transformSearchResults($request, $results, $parameters) |
||
116 | { |
||
117 | // before serializing to JSON, get rid of the Vocabulary object that came with each resource |
||
118 | foreach ($results as &$res) { |
||
119 | unset($res['voc']); |
||
120 | } |
||
121 | |||
122 | $context = array( |
||
123 | 'skos' => 'http://www.w3.org/2004/02/skos/core#', |
||
124 | 'isothes' => 'http://purl.org/iso25964/skos-thes#', |
||
125 | 'onki' => 'http://schema.onki.fi/onki#', |
||
126 | 'uri' => '@id', |
||
127 | 'type' => '@type', |
||
128 | 'results' => array( |
||
129 | '@id' => 'onki:results', |
||
130 | '@container' => '@list', |
||
131 | ), |
||
132 | 'prefLabel' => 'skos:prefLabel', |
||
133 | 'altLabel' => 'skos:altLabel', |
||
134 | 'hiddenLabel' => 'skos:hiddenLabel', |
||
135 | ); |
||
136 | foreach ($parameters->getAdditionalFields() as $field) { |
||
137 | |||
138 | // Quick-and-dirty compactification |
||
139 | $context[$field] = 'skos:' . $field; |
||
140 | foreach ($results as &$result) { |
||
141 | foreach ($result as $k => $v) { |
||
142 | if ($k == 'skos:' . $field) { |
||
143 | $result[$field] = $v; |
||
144 | unset($result['skos:' . $field]); |
||
145 | } |
||
146 | } |
||
147 | } |
||
148 | } |
||
149 | |||
150 | $ret = array( |
||
151 | '@context' => $context, |
||
152 | 'uri' => '', |
||
153 | 'results' => $results, |
||
154 | ); |
||
155 | |||
156 | if (isset($results[0]['prefLabels'])) { |
||
157 | $ret['@context']['prefLabels'] = array('@id' => 'skos:prefLabel', '@container' => '@language'); |
||
158 | } |
||
159 | |||
160 | if ($request->getQueryParam('labellang')) { |
||
161 | $ret['@context']['@language'] = $request->getQueryParam('labellang'); |
||
162 | } elseif ($request->getQueryParam('lang')) { |
||
163 | $ret['@context']['@language'] = $request->getQueryParam('lang'); |
||
164 | } |
||
165 | return $ret; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Performs the search function calls. And wraps the result in a json-ld object. |
||
170 | * @param Request $request |
||
171 | */ |
||
172 | public function search(Request $request): void |
||
196 | } |
||
197 | |||
198 | /** Vocabulary-specific methods **/ |
||
199 | |||
200 | /** |
||
201 | * Loads the vocabulary metadata. And wraps the result in a json-ld object. |
||
202 | * @param Request $request |
||
203 | */ |
||
204 | public function vocabularyInformation($request) |
||
205 | { |
||
206 | $vocab = $request->getVocab(); |
||
207 | if ($this->notModified($vocab)) { |
||
208 | return null; |
||
209 | } |
||
210 | |||
211 | /* encode the results in a JSON-LD compatible array */ |
||
212 | $conceptschemes = array(); |
||
213 | foreach ($vocab->getConceptSchemes($request->getLang()) as $uri => $csdata) { |
||
214 | $csdata['uri'] = $uri; |
||
215 | $csdata['type'] = 'skos:ConceptScheme'; |
||
216 | $conceptschemes[] = $csdata; |
||
217 | } |
||
218 | |||
219 | $ret = array( |
||
220 | '@context' => array( |
||
221 | 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', |
||
222 | 'skos' => 'http://www.w3.org/2004/02/skos/core#', |
||
223 | 'onki' => 'http://schema.onki.fi/onki#', |
||
224 | 'dct' => 'http://purl.org/dc/terms/', |
||
225 | 'uri' => '@id', |
||
226 | 'type' => '@type', |
||
227 | 'conceptschemes' => 'onki:hasConceptScheme', |
||
228 | 'id' => 'onki:vocabularyIdentifier', |
||
229 | 'defaultLanguage' => 'onki:defaultLanguage', |
||
230 | 'languages' => 'onki:language', |
||
231 | 'label' => 'rdfs:label', |
||
232 | 'prefLabel' => 'skos:prefLabel', |
||
233 | 'title' => 'dct:title', |
||
234 | '@language' => $request->getLang(), |
||
235 | '@base' => $this->getBaseHref() . "rest/v1/" . $vocab->getId() . "/", |
||
236 | ), |
||
237 | 'uri' => '', |
||
238 | 'id' => $vocab->getId(), |
||
239 | 'marcSource' => $vocab->getConfig()->getMarcSourceCode($request->getLang()), |
||
240 | 'title' => $vocab->getConfig()->getTitle($request->getLang()), |
||
241 | 'defaultLanguage' => $vocab->getConfig()->getDefaultLanguage(), |
||
242 | 'languages' => array_values($vocab->getConfig()->getLanguages()), |
||
243 | 'conceptschemes' => $conceptschemes, |
||
244 | ); |
||
245 | |||
246 | if ($vocab->getConfig()->getTypes($request->getLang())) { |
||
247 | $ret['type'] = $vocab->getConfig()->getTypes($request->getLang()); |
||
248 | } |
||
249 | |||
250 | return $this->returnJson($ret); |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Loads the vocabulary metadata. And wraps the result in a json-ld object. |
||
255 | * @param Request $request |
||
256 | */ |
||
257 | public function vocabularyStatistics($request) |
||
258 | { |
||
259 | if ($this->notModified($request->getVocab())) { |
||
260 | return null; |
||
261 | } |
||
262 | $this->model->setLocale($request->getLang()); |
||
263 | $arrayClass = $request->getVocab()->getConfig()->getArrayClassURI(); |
||
264 | $groupClass = $request->getVocab()->getConfig()->getGroupClassURI(); |
||
265 | $queryLang = $request->getQueryParam('lang') ?? $request->getLang(); |
||
266 | $vocabStats = $request->getVocab()->getStatistics($queryLang, $arrayClass, $groupClass); |
||
267 | $types = array('http://www.w3.org/2004/02/skos/core#Concept', 'http://www.w3.org/2004/02/skos/core#Collection', $arrayClass, $groupClass); |
||
268 | $subTypes = array(); |
||
269 | foreach ($vocabStats as $subtype) { |
||
270 | if (!in_array($subtype['type'], $types)) { |
||
271 | $subTypes[] = $subtype; |
||
272 | } |
||
273 | } |
||
274 | |||
275 | /* encode the results in a JSON-LD compatible array */ |
||
276 | $ret = array( |
||
277 | '@context' => array( |
||
278 | 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', |
||
279 | 'skos' => 'http://www.w3.org/2004/02/skos/core#', |
||
280 | 'void' => 'http://rdfs.org/ns/void#', |
||
281 | 'onki' => 'http://schema.onki.fi/onki#', |
||
282 | 'uri' => '@id', |
||
283 | 'id' => 'onki:vocabularyIdentifier', |
||
284 | 'concepts' => 'void:classPartition', |
||
285 | 'label' => 'rdfs:label', |
||
286 | 'class' => array('@id' => 'void:class', '@type' => '@id'), |
||
287 | 'subTypes' => array('@id' => 'void:class', '@type' => '@id'), |
||
288 | 'count' => 'void:entities', |
||
289 | '@language' => $request->getLang(), |
||
290 | '@base' => $this->getBaseHref() . "rest/v1/" . $request->getVocab()->getId() . "/", |
||
291 | ), |
||
292 | 'uri' => '', |
||
293 | 'id' => $request->getVocab()->getId(), |
||
294 | 'title' => $request->getVocab()->getConfig()->getTitle(), |
||
295 | 'concepts' => array( |
||
296 | 'class' => 'http://www.w3.org/2004/02/skos/core#Concept', |
||
297 | 'label' => $this->model->getText('skos:Concept'), |
||
298 | 'count' => isset($vocabStats['http://www.w3.org/2004/02/skos/core#Concept']) ? $vocabStats['http://www.w3.org/2004/02/skos/core#Concept']['count'] : 0, |
||
299 | 'deprecatedCount' => isset($vocabStats['http://www.w3.org/2004/02/skos/core#Concept']) ? $vocabStats['http://www.w3.org/2004/02/skos/core#Concept']['deprecatedCount'] : 0, |
||
300 | ), |
||
301 | 'subTypes' => $subTypes, |
||
302 | ); |
||
303 | |||
304 | if (isset($vocabStats['http://www.w3.org/2004/02/skos/core#Collection'])) { |
||
305 | $ret['conceptGroups'] = array( |
||
306 | 'class' => 'http://www.w3.org/2004/02/skos/core#Collection', |
||
307 | 'label' => $this->model->getText('skos:Collection'), |
||
308 | 'count' => $vocabStats['http://www.w3.org/2004/02/skos/core#Collection']['count'], |
||
309 | 'deprecatedCount' => $vocabStats['http://www.w3.org/2004/02/skos/core#Collection']['deprecatedCount'], |
||
310 | ); |
||
311 | |||
312 | } elseif (isset($vocabStats[$groupClass])) { |
||
313 | $ret['conceptGroups'] = array( |
||
314 | 'class' => $groupClass, |
||
315 | 'label' => isset($vocabStats[$groupClass]['label']) ? $vocabStats[$groupClass]['label'] : $this->model->getText(EasyRdf\RdfNamespace::shorten($groupClass)), |
||
316 | 'count' => $vocabStats[$groupClass]['count'], |
||
317 | 'deprecatedCount' => $vocabStats[$groupClass]['deprecatedCount'], |
||
318 | ); |
||
319 | } elseif (isset($vocabStats[$arrayClass])) { |
||
320 | $ret['arrays'] = array( |
||
321 | 'class' => $arrayClass, |
||
322 | 'label' => isset($vocabStats[$arrayClass]['label']) ? $vocabStats[$arrayClass]['label'] : $this->model->getText(EasyRdf\RdfNamespace::shorten($arrayClass)), |
||
323 | 'count' => $vocabStats[$arrayClass]['count'], |
||
324 | 'deprecatedCount' => $vocabStats[$arrayClass]['deprecatedCount'], |
||
325 | ); |
||
326 | } |
||
327 | |||
328 | return $this->returnJson($ret); |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * Loads the vocabulary metadata. And wraps the result in a json-ld object. |
||
333 | * @param Request $request |
||
334 | */ |
||
335 | public function labelStatistics($request) |
||
336 | { |
||
337 | if ($this->notModified($request->getVocab())) { |
||
338 | return null; |
||
339 | } |
||
340 | $lang = $request->getLang(); |
||
341 | $this->model->setLocale($request->getLang()); |
||
342 | $vocabStats = $request->getVocab()->getLabelStatistics(); |
||
343 | |||
344 | /* encode the results in a JSON-LD compatible array */ |
||
345 | $counts = array(); |
||
346 | foreach ($vocabStats['terms'] as $proplang => $properties) { |
||
347 | $langdata = array('language' => $proplang); |
||
348 | if ($lang) { |
||
349 | $langdata['literal'] = Punic\Language::getName($proplang, $lang); |
||
350 | } |
||
351 | |||
352 | $langdata['properties'] = array(); |
||
353 | foreach ($properties as $prop => $value) { |
||
354 | $langdata['properties'][] = array('property' => $prop, 'labels' => $value); |
||
355 | } |
||
356 | $counts[] = $langdata; |
||
357 | } |
||
358 | |||
359 | $ret = array( |
||
360 | '@context' => array( |
||
361 | 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', |
||
362 | 'skos' => 'http://www.w3.org/2004/02/skos/core#', |
||
363 | 'void' => 'http://rdfs.org/ns/void#', |
||
364 | 'void-ext' => 'http://ldf.fi/void-ext#', |
||
365 | 'onki' => 'http://schema.onki.fi/onki#', |
||
366 | 'uri' => '@id', |
||
367 | 'id' => 'onki:vocabularyIdentifier', |
||
368 | 'languages' => 'void-ext:languagePartition', |
||
369 | 'language' => 'void-ext:language', |
||
370 | 'properties' => 'void:propertyPartition', |
||
371 | 'labels' => 'void:triples', |
||
372 | '@base' => $this->getBaseHref() . "rest/v1/" . $request->getVocab()->getId() . "/", |
||
373 | ), |
||
374 | 'uri' => '', |
||
375 | 'id' => $request->getVocab()->getId(), |
||
376 | 'title' => $request->getVocab()->getConfig()->getTitle($lang), |
||
377 | 'languages' => $counts, |
||
378 | ); |
||
379 | |||
380 | if ($lang) { |
||
381 | $ret['@context']['literal'] = array('@id' => 'rdfs:label', '@language' => $lang); |
||
382 | } |
||
383 | |||
384 | return $this->returnJson($ret); |
||
385 | } |
||
386 | |||
387 | /** |
||
388 | * Loads the vocabulary type metadata. And wraps the result in a json-ld object. |
||
389 | * @param Request $request |
||
390 | */ |
||
391 | public function types($request) |
||
392 | { |
||
393 | $vocid = $request->getVocab() ? $request->getVocab()->getId() : null; |
||
394 | if ($vocid === null && !$request->getLang()) { |
||
395 | return $this->returnError(400, "Bad Request", "lang parameter missing"); |
||
396 | } |
||
397 | if ($this->notModified($request->getVocab())) { |
||
398 | return null; |
||
399 | } |
||
400 | |||
401 | $this->model->setLocale($request->getLang()); |
||
402 | |||
403 | $queriedtypes = $this->model->getTypes($vocid, $request->getLang()); |
||
404 | |||
405 | $types = array(); |
||
406 | |||
407 | /* encode the results in a JSON-LD compatible array */ |
||
408 | foreach ($queriedtypes as $uri => $typedata) { |
||
409 | $type = array_merge(array('uri' => $uri), $typedata); |
||
410 | $types[] = $type; |
||
411 | } |
||
412 | |||
413 | $base = $request->getVocab() ? $this->getBaseHref() . "rest/v1/" . $request->getVocab()->getId() . "/" : $this->getBaseHref() . "rest/v1/"; |
||
414 | |||
415 | $ret = array_merge_recursive( |
||
416 | $this->context, |
||
417 | array( |
||
418 | '@context' => array( |
||
419 | 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', |
||
420 | 'onki' => 'http://schema.onki.fi/onki#', |
||
421 | 'label' => 'rdfs:label', |
||
422 | 'superclass' => array('@id' => 'rdfs:subClassOf', '@type' => '@id'), |
||
423 | 'types' => 'onki:hasType', |
||
424 | '@language' => $request->getLang(), |
||
425 | '@base' => $base, |
||
426 | ), |
||
427 | 'uri' => '', |
||
428 | 'types' => $types) |
||
429 | ); |
||
430 | |||
431 | return $this->returnJson($ret); |
||
432 | } |
||
433 | |||
434 | private function findLookupHits($results, $label, $lang) |
||
435 | { |
||
436 | $hits = array(); |
||
437 | // case 1: exact match on preferred label |
||
438 | foreach ($results as $res) { |
||
439 | if (isset($res['prefLabel']) && $res['prefLabel'] == $label) { |
||
440 | $hits[] = $res; |
||
441 | } |
||
442 | } |
||
443 | if (sizeof($hits) > 0) { |
||
444 | return $hits; |
||
445 | } |
||
446 | |||
447 | // case 2: case-insensitive match on preferred label |
||
448 | foreach ($results as $res) { |
||
449 | if (isset($res['prefLabel']) && strtolower($res['prefLabel']) == strtolower($label)) { |
||
450 | $hits[] = $res; |
||
451 | } |
||
452 | } |
||
453 | if (sizeof($hits) > 0) { |
||
454 | return $hits; |
||
455 | } |
||
456 | |||
457 | if ($lang === null) { |
||
458 | // case 1A: exact match on preferred label in any language |
||
459 | foreach ($results as $res) { |
||
460 | if (isset($res['matchedPrefLabel']) && $res['matchedPrefLabel'] == $label) { |
||
461 | $res['prefLabel'] = $res['matchedPrefLabel']; |
||
462 | unset($res['matchedPrefLabel']); |
||
463 | $hits[] = $res; |
||
464 | } |
||
465 | } |
||
466 | if (sizeof($hits) > 0) { |
||
467 | return $hits; |
||
468 | } |
||
469 | |||
470 | // case 2A: case-insensitive match on preferred label in any language |
||
471 | foreach ($results as $res) { |
||
472 | if (isset($res['matchedPrefLabel']) && strtolower($res['matchedPrefLabel']) == strtolower($label)) { |
||
473 | $res['prefLabel'] = $res['matchedPrefLabel']; |
||
474 | unset($res['matchedPrefLabel']); |
||
475 | $hits[] = $res; |
||
476 | } |
||
477 | } |
||
478 | if (sizeof($hits) > 0) { |
||
479 | return $hits; |
||
480 | } |
||
481 | } |
||
482 | |||
483 | // case 3: exact match on alternate label |
||
484 | foreach ($results as $res) { |
||
485 | if (isset($res['altLabel']) && $res['altLabel'] == $label) { |
||
486 | $hits[] = $res; |
||
487 | } |
||
488 | } |
||
489 | if (sizeof($hits) > 0) { |
||
490 | return $hits; |
||
491 | } |
||
492 | |||
493 | |||
494 | // case 4: case-insensitive match on alternate label |
||
495 | foreach ($results as $res) { |
||
496 | if (isset($res['altLabel']) && strtolower($res['altLabel']) == strtolower($label)) { |
||
497 | $hits[] = $res; |
||
498 | } |
||
499 | } |
||
500 | if (sizeof($hits) > 0) { |
||
501 | return $hits; |
||
502 | } |
||
503 | |||
504 | return $hits; |
||
505 | } |
||
506 | |||
507 | private function transformLookupResults($lang, $hits) |
||
508 | { |
||
509 | if (sizeof($hits) == 0) { |
||
510 | // no matches found |
||
511 | return; |
||
512 | } |
||
513 | |||
514 | // found matches, getting rid of Vocabulary objects |
||
515 | foreach ($hits as &$res) { |
||
516 | unset($res['voc']); |
||
517 | } |
||
518 | |||
519 | $ret = array_merge_recursive( |
||
520 | $this->context, |
||
521 | array( |
||
522 | '@context' => array('onki' => 'http://schema.onki.fi/onki#', 'results' => array('@id' => 'onki:results'), 'prefLabel' => 'skos:prefLabel', 'altLabel' => 'skos:altLabel', 'hiddenLabel' => 'skos:hiddenLabel'), |
||
523 | 'result' => $hits) |
||
524 | ); |
||
525 | |||
526 | if ($lang) { |
||
527 | $ret['@context']['@language'] = $lang; |
||
528 | } |
||
529 | |||
530 | return $ret; |
||
531 | } |
||
532 | |||
533 | /** |
||
534 | * Used for finding terms by their exact prefLabel. Wraps the result in a json-ld object. |
||
535 | * @param Request $request |
||
536 | */ |
||
537 | public function lookup($request) |
||
538 | { |
||
539 | $label = $request->getQueryParamRaw('label'); |
||
540 | if (!$label) { |
||
541 | return $this->returnError(400, "Bad Request", "label parameter missing"); |
||
542 | } |
||
543 | |||
544 | $lang = $request->getQueryParam('lang'); |
||
545 | $parameters = new ConceptSearchParameters($request, $this->model->getConfig(), true); |
||
546 | $results = $this->model->searchConcepts($parameters); |
||
547 | $hits = $this->findLookupHits($results, $label, $lang); |
||
548 | $ret = $this->transformLookupResults($lang, $hits); |
||
549 | if ($ret === null) { |
||
550 | return $this->returnError(404, 'Not Found', "Could not find label '$label'"); |
||
551 | } |
||
552 | return $this->returnJson($ret); |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * Queries the top concepts of a vocabulary and wraps the results in a json-ld object. |
||
557 | * @param Request $request |
||
558 | * @return object json-ld object |
||
559 | */ |
||
560 | public function topConcepts($request) |
||
561 | { |
||
562 | $vocab = $request->getVocab(); |
||
563 | if ($this->notModified($vocab)) { |
||
564 | return null; |
||
565 | } |
||
566 | $scheme = $request->getQueryParam('scheme'); |
||
567 | if (!$scheme) { |
||
568 | $scheme = $vocab->getConfig()->showConceptSchemesInHierarchy() ? array_keys($vocab->getConceptSchemes()) : $vocab->getDefaultConceptScheme(); |
||
569 | } |
||
570 | |||
571 | /* encode the results in a JSON-LD compatible array */ |
||
572 | $topconcepts = $vocab->getTopConcepts($scheme, $request->getLang()); |
||
573 | |||
574 | $ret = array_merge_recursive( |
||
575 | $this->context, |
||
576 | array( |
||
577 | '@context' => array('onki' => 'http://schema.onki.fi/onki#', 'topconcepts' => 'skos:hasTopConcept', 'notation' => 'skos:notation', 'label' => 'skos:prefLabel', '@language' => $request->getLang()), |
||
578 | 'uri' => $scheme, |
||
579 | 'topconcepts' => $topconcepts) |
||
580 | ); |
||
581 | |||
582 | return $this->returnJson($ret); |
||
583 | } |
||
584 | |||
585 | private function redirectToVocabData($request) |
||
586 | { |
||
587 | $urls = $request->getVocab()->getConfig()->getDataURLs(); |
||
588 | if (sizeof($urls) == 0) { |
||
589 | $vocid = $request->getVocab()->getId(); |
||
590 | return $this->returnError('404', 'Not Found', "No download source URL known for vocabulary $vocid"); |
||
591 | } |
||
592 | |||
593 | $format = $this->negotiateFormat(array_keys($urls), $request->getServerConstant('HTTP_ACCEPT'), $request->getQueryParam('format')); |
||
594 | if (!$format) { |
||
595 | return $this->returnError(406, 'Not Acceptable', "Unsupported format. Supported MIME types are: " . implode(' ', array_keys($urls))); |
||
596 | } |
||
597 | if (is_array($urls[$format])) { |
||
598 | $arr = $urls[$format]; |
||
599 | $dataLang = $request->getLang(); |
||
600 | if (isset($arr[$dataLang])) { |
||
601 | header("Location: " . $arr[$dataLang]); |
||
602 | } else { |
||
603 | $vocid = $request->getVocab()->getId(); |
||
604 | return $this->returnError('404', 'Not Found', "No download source URL known for vocabulary $vocid in language $dataLang"); |
||
605 | } |
||
606 | } else { |
||
607 | header("Location: " . $urls[$format]); |
||
608 | } |
||
609 | } |
||
610 | |||
611 | private function returnDataResults($results, $format) |
||
647 | } |
||
648 | |||
649 | /** |
||
650 | * Download a concept as json-ld or redirect to download the whole vocabulary. |
||
651 | * @param Request $request |
||
652 | * @return object json-ld formatted concept. |
||
653 | */ |
||
654 | public function data($request) |
||
680 | } |
||
681 | |||
682 | /** |
||
683 | * Get the mappings associated with a concept, enriched with labels and notations. |
||
684 | * Returns a JSKOS-compatible JSON object. |
||
685 | * @param Request $request |
||
686 | * @throws Exception if the vocabulary ID is not found in configuration |
||
687 | */ |
||
688 | public function mappings(Request $request) |
||
689 | { |
||
690 | $this->model->setLocale($request->getLang()); |
||
691 | $vocab = $request->getVocab(); |
||
692 | if ($this->notModified($vocab)) { |
||
693 | return null; |
||
694 | } |
||
695 | |||
696 | $uri = $request->getUri(); |
||
697 | if (!$uri) { |
||
698 | return $this->returnError(400, 'Bad Request', "uri parameter missing"); |
||
699 | } |
||
700 | |||
701 | $queryExVocabs = $request->getQueryParamBoolean('external', true); |
||
702 | |||
703 | $concept = $vocab->getConceptInfo($uri, $request->getContentLang()); |
||
704 | if (empty($concept)) { |
||
705 | return $this->returnError(404, 'Bad Request', "no concept found with given uri"); |
||
706 | } |
||
707 | |||
708 | $mappings = []; |
||
709 | $linkUrlExtension = new LinkUrlExtension($this->model); |
||
710 | foreach ($concept->getMappingProperties() as $mappingProperty) { |
||
711 | foreach ($mappingProperty->getValues() as $mappingPropertyValue) { |
||
712 | $hrefLink = $linkUrlExtension->linkUrlFilter($mappingPropertyValue->getUri(), $mappingPropertyValue->getExVocab(), $request->getLang(), 'page', $request->getContentLang()); |
||
713 | $mappings[] = $mappingPropertyValue->asJskos($queryExVocabs, $request->getLang(), $hrefLink); |
||
714 | } |
||
715 | } |
||
716 | |||
717 | $ret = array( |
||
718 | 'mappings' => $mappings, |
||
719 | 'graph' => $concept->dumpJsonLd() |
||
720 | ); |
||
721 | |||
722 | return $this->returnJson($ret); |
||
723 | } |
||
724 | |||
725 | /** |
||
726 | * Used for querying labels for a uri. |
||
727 | * @param Request $request |
||
728 | * @return object json-ld wrapped labels. |
||
729 | */ |
||
730 | public function label($request) |
||
731 | { |
||
732 | if (!$request->getUri()) { |
||
733 | return $this->returnError(400, "Bad Request", "uri parameter missing"); |
||
734 | } |
||
735 | |||
736 | if ($this->notModified($request->getVocab())) { |
||
737 | return null; |
||
738 | } |
||
739 | |||
740 | $vocab = $request->getVocab(); |
||
741 | if ($vocab === null) { |
||
742 | $vocab = $this->model->guessVocabularyFromUri($request->getUri()); |
||
743 | } |
||
744 | if ($vocab === null) { |
||
745 | return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>"); |
||
746 | } |
||
747 | |||
748 | $labelResults = $vocab->getAllConceptLabels($request->getUri(), $request->getLang()); |
||
749 | if ($labelResults === null) { |
||
750 | return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>"); |
||
751 | } |
||
752 | |||
753 | // there should be only one preferred label so no need for an array |
||
754 | if (array_key_exists('prefLabel', $labelResults)) { |
||
755 | $labelResults['prefLabel'] = $labelResults['prefLabel'][0]; |
||
756 | } |
||
757 | |||
758 | $ret = array_merge_recursive( |
||
759 | $this->context, |
||
760 | array('@context' => array('prefLabel' => 'skos:prefLabel', 'altLabel' => 'skos:altLabel', 'hiddenLabel' => 'skos:hiddenLabel', '@language' => $request->getLang()), |
||
761 | 'uri' => $request->getUri()), |
||
762 | $labelResults |
||
763 | ); |
||
764 | |||
765 | return $this->returnJson($ret); |
||
766 | } |
||
767 | |||
768 | /** |
||
769 | * Query for the available letters in the alphabetical index. |
||
770 | * @param Request $request |
||
771 | * @return object JSON-LD wrapped list of letters |
||
772 | */ |
||
773 | |||
774 | public function indexLetters($request) |
||
793 | } |
||
794 | |||
795 | /** |
||
796 | * Query for the concepts with terms starting with a given letter in the |
||
797 | * alphabetical index. |
||
798 | * @param Request $request |
||
799 | * @return object JSON-LD wrapped list of terms/concepts |
||
800 | */ |
||
801 | |||
802 | public function indexConcepts($letter, $request) |
||
803 | { |
||
804 | $this->model->setLocale($request->getLang()); |
||
805 | |||
806 | $offset_param = $request->getQueryParam('offset'); |
||
807 | $offset = (is_numeric($offset_param) && $offset_param >= 0) ? $offset_param : 0; |
||
808 | $limit_param = $request->getQueryParam('limit'); |
||
809 | $limit = (is_numeric($limit_param) && $limit_param >= 0) ? $limit_param : 0; |
||
810 | |||
811 | $concepts = $request->getVocab()->searchConceptsAlphabetical($letter, $limit, $offset, $request->getLang()); |
||
812 | |||
813 | $ret = array_merge_recursive( |
||
814 | $this->context, |
||
815 | array( |
||
816 | '@context' => array( |
||
817 | 'indexConcepts' => array( |
||
818 | '@id' => 'skosmos:indexConcepts', |
||
819 | '@container' => '@list' |
||
820 | ) |
||
821 | ), |
||
822 | 'uri' => '', |
||
823 | 'indexConcepts' => $concepts) |
||
824 | ); |
||
825 | return $this->returnJson($ret); |
||
826 | } |
||
827 | |||
828 | private function transformPropertyResults($uri, $lang, $objects, $propname, $propuri) |
||
829 | { |
||
830 | $results = array(); |
||
831 | foreach ($objects as $objuri => $vals) { |
||
832 | $results[] = array('uri' => $objuri, 'prefLabel' => $vals['label']); |
||
833 | } |
||
834 | |||
835 | return array_merge_recursive( |
||
836 | $this->context, |
||
837 | array( |
||
838 | '@context' => array('prefLabel' => 'skos:prefLabel', $propname => $propuri, '@language' => $lang), |
||
839 | 'uri' => $uri, |
||
840 | $propname => $results) |
||
841 | ); |
||
842 | } |
||
843 | |||
844 | private function transformTransitivePropertyResults($uri, $lang, $objects, $tpropname, $tpropuri, $dpropname, $dpropuri) |
||
861 | ); |
||
862 | } |
||
863 | |||
864 | /** |
||
865 | * Used for querying broader relations for a concept. |
||
866 | * @param Request $request |
||
867 | * @return object json-ld wrapped broader concept uris and labels. |
||
868 | */ |
||
869 | public function broader($request) |
||
870 | { |
||
871 | if ($this->notModified($request->getVocab())) { |
||
872 | return null; |
||
873 | } |
||
874 | $broaders = $request->getVocab()->getConceptBroaders($request->getUri(), $request->getLang()); |
||
875 | if ($broaders === null) { |
||
876 | return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>"); |
||
877 | } |
||
878 | $ret = $this->transformPropertyResults($request->getUri(), $request->getLang(), $broaders, "broader", "skos:broader"); |
||
879 | return $this->returnJson($ret); |
||
880 | } |
||
881 | |||
882 | /** |
||
883 | * Used for querying broader transitive relations for a concept. |
||
884 | * @param Request $request |
||
885 | * @return object json-ld wrapped broader transitive concept uris and labels. |
||
886 | */ |
||
887 | public function broaderTransitive($request) |
||
888 | { |
||
889 | if ($this->notModified($request->getVocab())) { |
||
890 | return null; |
||
891 | } |
||
892 | $broaders = $request->getVocab()->getConceptTransitiveBroaders($request->getUri(), $this->parseLimit(), false, $request->getLang()); |
||
893 | if (empty($broaders)) { |
||
894 | return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>"); |
||
895 | } |
||
896 | $ret = $this->transformTransitivePropertyResults($request->getUri(), $request->getLang(), $broaders, "broaderTransitive", "skos:broaderTransitive", "broader", "skos:broader"); |
||
897 | return $this->returnJson($ret); |
||
898 | } |
||
899 | |||
900 | /** |
||
901 | * Used for querying narrower relations for a concept. |
||
902 | * @param Request $request |
||
903 | * @return object json-ld wrapped narrower concept uris and labels. |
||
904 | */ |
||
905 | public function narrower($request) |
||
906 | { |
||
907 | if ($this->notModified($request->getVocab())) { |
||
908 | return null; |
||
909 | } |
||
910 | $narrowers = $request->getVocab()->getConceptNarrowers($request->getUri(), $request->getLang()); |
||
911 | if ($narrowers === null) { |
||
912 | return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>"); |
||
913 | } |
||
914 | $ret = $this->transformPropertyResults($request->getUri(), $request->getLang(), $narrowers, "narrower", "skos:narrower"); |
||
915 | return $this->returnJson($ret); |
||
916 | } |
||
917 | |||
918 | /** |
||
919 | * Used for querying narrower transitive relations for a concept. |
||
920 | * @param Request $request |
||
921 | * @return object json-ld wrapped narrower transitive concept uris and labels. |
||
922 | */ |
||
923 | public function narrowerTransitive($request) |
||
934 | } |
||
935 | |||
936 | /** |
||
937 | * Used for querying broader transitive relations |
||
938 | * and some narrowers for a concept in the hierarchy view. |
||
939 | * @param Request $request |
||
940 | * @return object json-ld wrapped hierarchical concept uris and labels. |
||
941 | */ |
||
942 | public function hierarchy($request) |
||
1018 | } |
||
1019 | |||
1020 | /** |
||
1021 | * Used for querying group hierarchy for the sidebar group view. |
||
1022 | * @param Request $request |
||
1023 | * @return object json-ld wrapped hierarchical concept uris and labels. |
||
1024 | */ |
||
1025 | public function groups($request) |
||
1041 | } |
||
1042 | |||
1043 | /** |
||
1044 | * Used for querying member relations for a group. |
||
1045 | * @param Request $request |
||
1046 | * @return object json-ld wrapped narrower concept uris and labels. |
||
1047 | */ |
||
1048 | public function groupMembers($request) |
||
1049 | { |
||
1050 | if ($this->notModified($request->getVocab())) { |
||
1051 | return null; |
||
1052 | } |
||
1053 | $children = $request->getVocab()->listConceptGroupContents($request->getUri(), $request->getLang()); |
||
1054 | if (empty($children)) { |
||
1055 | return $this->returnError('404', 'Not Found', "Could not find group <{$request->getUri()}>"); |
||
1056 | } |
||
1057 | |||
1058 | $ret = array_merge_recursive( |
||
1059 | $this->context, |
||
1060 | array( |
||
1061 | '@context' => array('prefLabel' => 'skos:prefLabel', 'members' => 'skos:member', '@language' => $request->getLang()), |
||
1062 | 'uri' => $request->getUri(), |
||
1063 | 'members' => $children) |
||
1064 | ); |
||
1065 | |||
1066 | return $this->returnJson($ret); |
||
1067 | } |
||
1068 | |||
1069 | /** |
||
1070 | * Used for querying narrower relations for a concept in the hierarchy view. |
||
1071 | * @param Request $request |
||
1072 | * @return object json-ld wrapped narrower concept uris and labels. |
||
1073 | */ |
||
1074 | public function children($request) |
||
1075 | { |
||
1076 | if ($this->notModified($request->getVocab())) { |
||
1077 | return null; |
||
1078 | } |
||
1079 | $children = $request->getVocab()->getConceptChildren($request->getUri(), $request->getLang()); |
||
1080 | if ($children === null) { |
||
1081 | return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>"); |
||
1082 | } |
||
1083 | |||
1084 | $ret = array_merge_recursive( |
||
1085 | $this->context, |
||
1086 | array( |
||
1087 | '@context' => array('prefLabel' => 'skos:prefLabel', 'narrower' => 'skos:narrower', 'notation' => 'skos:notation', 'hasChildren' => 'onki:hasChildren', '@language' => $request->getLang()), |
||
1088 | 'uri' => $request->getUri(), |
||
1089 | 'narrower' => $children) |
||
1090 | ); |
||
1091 | |||
1092 | return $this->returnJson($ret); |
||
1093 | } |
||
1094 | |||
1095 | /** |
||
1096 | * Used for querying narrower relations for a concept in the hierarchy view. |
||
1097 | * @param Request $request |
||
1098 | * @return object json-ld wrapped hierarchical concept uris and labels. |
||
1099 | */ |
||
1100 | public function related($request) |
||
1101 | { |
||
1102 | if ($this->notModified($request->getVocab())) { |
||
1103 | return null; |
||
1104 | } |
||
1105 | $related = $request->getVocab()->getConceptRelateds($request->getUri(), $request->getLang()); |
||
1106 | if ($related === null) { |
||
1107 | return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>"); |
||
1108 | } |
||
1109 | $ret = $this->transformPropertyResults($request->getUri(), $request->getLang(), $related, "related", "skos:related"); |
||
1110 | return $this->returnJson($ret); |
||
1111 | } |
||
1112 | |||
1113 | /** |
||
1114 | * Used for querying new concepts in the vocabulary |
||
1115 | * @param Request $request |
||
1116 | * @return object json-ld wrapped list of changed concepts |
||
1117 | */ |
||
1118 | public function newConcepts($request) |
||
1119 | { |
||
1120 | $offset = ($request->getQueryParam('offset') && is_numeric($request->getQueryParam('offset')) && $request->getQueryParam('offset') >= 0) ? $request->getQueryParam('offset') : 0; |
||
1121 | $limit = ($request->getQueryParam('limit') && is_numeric($request->getQueryParam('limit')) && $request->getQueryParam('limit') >= 0) ? $request->getQueryParam('limit') : 200; |
||
1122 | |||
1123 | return $this->changedConcepts($request, 'dc:created', $offset, $limit); |
||
1124 | } |
||
1125 | |||
1126 | /** |
||
1127 | * Used for querying modified concepts in the vocabulary |
||
1128 | * @param Request $request |
||
1129 | * @return object json-ld wrapped list of changed concepts |
||
1130 | */ |
||
1131 | public function modifiedConcepts($request) |
||
1132 | { |
||
1133 | $offset = ($request->getQueryParam('offset') && is_numeric($request->getQueryParam('offset')) && $request->getQueryParam('offset') >= 0) ? $request->getQueryParam('offset') : 0; |
||
1134 | $limit = ($request->getQueryParam('limit') && is_numeric($request->getQueryParam('limit')) && $request->getQueryParam('limit') >= 0) ? $request->getQueryParam('limit') : 200; |
||
1135 | |||
1136 | return $this->changedConcepts($request, 'dc:modified', $offset, $limit); |
||
1137 | } |
||
1138 | |||
1139 | /** |
||
1140 | * Used for querying changed concepts in the vocabulary |
||
1141 | * @param Request $request |
||
1142 | * @param int $offset starting index offset |
||
1143 | * @param int $limit maximum number of concepts to return |
||
1144 | * @return object json-ld wrapped list of changed concepts |
||
1145 | */ |
||
1146 | private function changedConcepts($request, $prop, $offset, $limit) |
||
1174 | )); |
||
1175 | |||
1176 | } |
||
1177 | } |
||
1178 |