ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
Importer.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
28
30{
31 protected const string PATH_TO_SCHEMA = __DIR__ . '/../../../../VocabValidation/controlled_vocabulary.xsd';
32
33 protected PathFactory $path_factory;
34 protected ControlledVocabsRepository $vocab_repo;
35 protected SlotHandler $slot_handler;
36
37 public function __construct(
38 PathFactory $path_factory,
39 ControlledVocabsRepository $vocab_repo,
40 SlotHandler $slot_handler
41 ) {
42 $this->path_factory = $path_factory;
43 $this->vocab_repo = $vocab_repo;
44 $this->slot_handler = $slot_handler;
45 }
46
47 public function import(string $xml_string): Result
48 {
49 $errors_or_xml = $this->loadXML($xml_string);
50 if (is_array($errors_or_xml)) {
51 return new Result(...$errors_or_xml);
52 }
53 $xml_path = new \DOMXPath($errors_or_xml);
54 $errors = [];
55
56 try {
57 $slot = $this->extractVocabularySlot($xml_path);
58 } catch (\ilMDPathException $e) {
59 $errors[] = $e->getMessage();
60 }
61 if (isset($slot) && $slot === SlotIdentifier::NULL) {
62 $errors[] = 'Cannot add vocabulary, invalid element or condition.';
63 }
64
65 $duplicates = $this->findDuplicateValues($xml_path);
66 if (!empty($duplicates)) {
67 $errors[] = 'The following values are not unique: ' . implode(', ', $duplicates);
68 }
69 if (empty($errors) && isset($slot)) {
70 $already_exist = $this->findAlreadyExistingValues($xml_path, $slot);
71 if (!empty($already_exist)) {
72 $errors[] = 'The following values already exist in other vocabularies of the element: ' .
73 implode(', ', $already_exist);
74 }
75 }
76
77 if (empty($errors) && isset($slot)) {
78 try {
79 $vocab_id = $this->createVocabulary(
80 $slot,
81 $this->extractSource($xml_path)
82 );
83 } catch (\ilMDVocabulariesException $e) {
84 $errors[] = $e->getMessage();
85 }
86 }
87 if (empty($errors) && isset($vocab_id)) {
88 $this->addValuesToVocabulary($xml_path, $vocab_id);
89 }
90
91 return new Result(...$errors);
92 }
93
98 protected function loadXML(string $xml_string): \DOMDocument|array
99 {
100 $use_internal_errors = libxml_use_internal_errors(true);
101
102 $xml = new \DOMDocument('1.0', 'utf-8');
103 $xml->loadXML($xml_string);
104
105 if (!$xml->schemaValidate(self::PATH_TO_SCHEMA)) {
106 $errors = [];
107 foreach (libxml_get_errors() as $error) {
108 $errors[] = $error->message;
109 }
110 }
111
112 libxml_clear_errors();
113 libxml_use_internal_errors($use_internal_errors);
114
115 return empty($errors) ? $xml : $errors;
116 }
117
118 protected function extractPathToElement(\DOMXPath $xml_path): PathInterface
119 {
120 $node = $xml_path->query('//vocabulary/appliesTo/pathToElement')->item(0);
121 return $this->writeToPath($node);
122 }
123
124 protected function extractPathToCondition(\DOMXPath $xml_path): ?PathInterface
125 {
126 $node = $xml_path->query('//vocabulary/appliesTo/condition/pathToElement')->item(0);
127 return is_null($node) ? null : $this->writeToPath($node, true);
128 }
129
130 protected function extractConditionValue(\DOMXPath $xml_path): ?string
131 {
132 $node = $xml_path->query('//vocabulary/appliesTo/condition/@value')->item(0);
133 return $node?->nodeValue;
134 }
135
136 protected function extractVocabularySlot(\DOMXPath $xml_path): SlotIdentifier
137 {
138 $path_to_element = $this->extractPathToElement($xml_path);
139 $path_to_condition = $this->extractPathToCondition($xml_path);
140 $condition_value = $this->extractConditionValue($xml_path);
141
142 return $this->slot_handler->identiferFromPathAndCondition(
143 $path_to_element,
144 $path_to_condition,
145 $condition_value
146 );
147 }
148
149 protected function extractSource(\DOMXPath $xml_path): string
150 {
151 $node = $xml_path->query('//vocabulary/source')->item(0);
152 return (string) $node?->nodeValue;
153 }
154
158 protected function extractValuesAndLabels(\DOMXPath $xml_path): \Generator
159 {
160 $nodes = $xml_path->query('//vocabulary/values/value');
161 foreach ($nodes as $node) {
162 $label = $node->hasAttribute('label') ? $node->getAttribute('label') : '';
163 $value = $node->nodeValue;
164 yield $value => $label;
165 }
166 }
167
171 protected function findDuplicateValues(\DOMXPath $xml_path): array
172 {
173 $values = [];
174 $duplicates = [];
175 foreach ($this->extractValuesAndLabels($xml_path) as $value => $label) {
176 if (in_array($value, $values) && !in_array($value, $duplicates)) {
177 $duplicates[] = $value;
178 }
179 $values[] = $value;
180 }
181
182 return $duplicates;
183 }
184
188 protected function findAlreadyExistingValues(
189 \DOMXPath $xml_path,
190 SlotIdentifier $slot
191 ): array {
192 $values = [];
193 foreach ($this->extractValuesAndLabels($xml_path) as $value => $label) {
194 $values[] = $value;
195 }
196 return iterator_to_array($this->vocab_repo->findAlreadyExistingValues(
197 $slot,
198 ...$values
199 ));
200 }
201
205 protected function createVocabulary(
206 SlotIdentifier $slot,
207 string $source
208 ): string {
209 return $this->vocab_repo->create($slot, $source);
210 }
211
212 protected function addValuesToVocabulary(
213 \DOMXPath $xml_path,
214 string $vocab_id
215 ): void {
216 foreach ($this->extractValuesAndLabels($xml_path) as $value => $label) {
217 $this->vocab_repo->addValueToVocabulary(
218 $vocab_id,
219 $value,
220 $label
221 );
222 }
223 }
224
225 protected function writeToPath(\DOMElement $path_in_xml, bool $relative = false): PathInterface
226 {
227 $builder = $this->path_factory->custom();
228 foreach ($path_in_xml->childNodes as $step) {
229 if (!($step instanceof \DOMElement)) {
230 continue;
231 }
232 if ($step->nodeName === 'step') {
233 $builder = $builder->withNextStep($step->nodeValue);
234 } elseif ($step->nodeName === 'stepToSuper') {
235 $builder = $builder->withNextStepToSuperElement();
236 }
237 }
238 return $builder->withRelative($relative)->get();
239 }
240}
__construct(PathFactory $path_factory, ControlledVocabsRepository $vocab_repo, SlotHandler $slot_handler)
Definition: Importer.php:37
loadXML(string $xml_string)
Returns the xml or errors.
Definition: Importer.php:98
writeToPath(\DOMElement $path_in_xml, bool $relative=false)
Definition: Importer.php:225
extractValuesAndLabels(\DOMXPath $xml_path)
Yields value => label as strings.
Definition: Importer.php:158
createVocabulary(SlotIdentifier $slot, string $source)
Returns vocab ID.
Definition: Importer.php:205
findAlreadyExistingValues(\DOMXPath $xml_path, SlotIdentifier $slot)
Definition: Importer.php:188
addValuesToVocabulary(\DOMXPath $xml_path, string $vocab_id)
Definition: Importer.php:212
ilErrorHandling $error
Definition: class.ilias.php:69
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...