ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
Manipulator.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
26use ILIAS\MetaData\Paths\FactoryInterface as PathFactoryInterface;
40
42{
46 protected PathFactoryInterface $path_factory;
48
49 public function __construct(
53 PathFactoryInterface $path_factory,
55 ) {
56 $this->scaffold_provider = $scaffold_provider;
57 $this->marker_factory = $marker_factory;
58 $this->navigator_factory = $navigator_factory;
59 $this->path_factory = $path_factory;
60 $this->path_utilities_factory = $path_utilities_factory;
61 }
62
66 public function prepareDelete(
67 SetInterface $set,
69 ): SetInterface {
73 $my_set = clone $set;
74 $elements = $this->navigator_factory->navigator($path, $my_set->getRoot())->elementsAtFinalStep();
75 $target_elements = [];
76 foreach ($elements as $element) {
77 if ($element instanceof MarkableInterface) {
78 $target_elements[] = $element;
79 }
80 }
81 $this->markElementsDelete($target_elements);
82 return $my_set;
83 }
84
88 public function prepareCreateOrUpdate(
89 SetInterface $set,
91 string ...$values
92 ): SetInterface {
93 $my_set = clone $set;
94 $elements_to_update = $this->getElementsToUpdate($my_set->getRoot(), $path, ...$values);
95 $remaining_values = array_slice($values, 0, count($values) - count($elements_to_update));
96 $elements_to_create = $this->getElementsToCreate($my_set->getRoot(), $path, ...$remaining_values);
97 $target_elements = array_merge($elements_to_update, $elements_to_create);
98 $this->markElementsCreateOrUpdate($target_elements, $values);
99 return $my_set;
100 }
101
105 public function prepareForceCreate(
106 SetInterface $set,
108 string ...$values
109 ): SetInterface {
110 $my_set = clone $set;
111 $target_elements = $this->getElementsToCreate($my_set->getRoot(), $path, ...$values);
112 $this->markElementsCreateOrUpdate($target_elements, $values);
113 return $my_set;
114 }
115
116 protected function getElementsToUpdate(
117 ElementInterface $set_root,
119 string ...$values
120 ): array {
125 $path_conditions = $this->path_utilities_factory->pathConditionsCollection($path);
126 $path_checker = $this->path_utilities_factory->pathConditionChecker($path_conditions);
127 $target_elements = [];
128 $navigators = [$this->navigator_factory->navigator($path_conditions->getPathWithoutConditions(), $set_root)];
129
130 if (count($values) <= 0) {
131 return [];
132 }
133
134 // Search for existing elements to update
135 while (count($navigators) > 0) {
136 $curr_navi = array_shift($navigators)->nextStep();
137 $at_least_one_path_condition_met = $path_checker->atLeastOnePathConditionIsMet(
138 $curr_navi->currentStep(),
139 ...$curr_navi->elements()
140 );
141
142 // Complete Path: Target elements at end of path found
143 if ($at_least_one_path_condition_met && $curr_navi->hasElements() && !$curr_navi->hasNextStep()) {
144 $roots = iterator_to_array($path_checker->getRootsThatMeetPathCondition(
145 $curr_navi->currentStep(),
146 ...$curr_navi->elements()
147 ));
148 // Slice array to select only as many elements as needed
149 $missing_target_count = max(0, count($values) - count($target_elements));
150 $targets = array_slice($roots, 0, $missing_target_count);
151 array_push($target_elements, ...$targets);
152 continue;
153 }
154
155 // Path Continues: Add navigators with the elements that meet the path conditions as roots
156 if ($at_least_one_path_condition_met && $curr_navi->hasElements() && $curr_navi->hasNextStep()) {
157 $roots = iterator_to_array($path_checker->getRootsThatMeetPathCondition(
158 $curr_navi->currentStep(),
159 ...$curr_navi->elements()
160 ));
161 foreach ($roots as $root) {
162 $navigators[] = $this->navigator_factory->navigator(
163 $this->remainingPathOfNavigator($curr_navi->nextStep()),
164 $root
165 );
166 }
167 }
168 }
169
170 return $target_elements;
171 }
172
173 protected function getElementsToCreate(
174 ElementInterface $set_root,
175 PathInterface $path,
176 string ...$values
177 ): array {
184 $path_conditions = $this->path_utilities_factory->pathConditionsCollection($path);
185 $path_checker = $this->path_utilities_factory->pathConditionChecker($path_conditions);
186 $target_elements = [];
187 $loose_end_roots = [];
188 $loose_end_paths = [];
189 $navigators = [$this->navigator_factory->navigator($path_conditions->getPathWithoutConditions(), $set_root)];
190
191 if (count($values) <= 0) {
192 return [];
193 }
194
195 // Elements to create
196 while (count($navigators) > 0) {
197 $orig_navi = array_shift($navigators);
198 $curr_navi = $orig_navi->nextStep();
199 $at_least_one_path_condition_met = $path_checker->atLeastOnePathConditionIsMet(
200 $curr_navi->currentStep(),
201 ...$curr_navi->elements()
202 );
203
204 // Path Conditions not met
205 if (!$at_least_one_path_condition_met) {
206 $roots = iterator_to_array($orig_navi->elements());
207 $paths = array_fill(0, count($roots), $this->remainingPathOfNavigator($orig_navi));
208 array_push($loose_end_roots, ...$roots);
209 array_push($loose_end_paths, ...$paths);
210 continue;
211 }
212
213 // Incomplete Path: At end of path but target elements are missing
214 if ($at_least_one_path_condition_met && !$curr_navi->hasElements() && !$curr_navi->hasNextStep()) {
215 $roots = iterator_to_array($orig_navi->elements());
216 $paths = array_fill(0, count($roots), $this->remainingPathOfNavigator($orig_navi->nextStep()));
217 array_push($loose_end_roots, ...$roots);
218 array_push($loose_end_paths, ...$paths);
219 continue;
220 }
221
222 // Incomplete Path: Complete the paths as needed
223 if ($at_least_one_path_condition_met && !$curr_navi->hasElements() && $curr_navi->hasNextStep()) {
224 $roots = iterator_to_array($path_checker->getRootsThatMeetPathCondition(
225 $curr_navi->currentStep(),
226 ...$curr_navi->elements()
227 ));
228 $paths = array_fill(0, count($roots), $this->remainingPathOfNavigator($curr_navi->nextStep()));
229 array_push($loose_end_roots, ...$roots);
230 array_push($loose_end_paths, ...$paths);
231 continue;
232 }
233
234 // Path Continues: Add navigators with the elements that meet the path conditions as roots
235 if ($at_least_one_path_condition_met && $curr_navi->hasElements() && $curr_navi->hasNextStep()) {
236 $roots = iterator_to_array($path_checker->getRootsThatMeetPathCondition(
237 $curr_navi->currentStep(),
238 ...$curr_navi->elements()
239 ));
240 foreach ($roots as $root) {
241 $navigators[] = $this->navigator_factory->navigator(
242 $this->remainingPathOfNavigator($curr_navi->nextStep()),
243 $root
244 );
245 }
246 }
247 }
248
249 // Create the path if an incomplete path was found
250 $missing_target_count = max(0, count($values) - count($target_elements));
251 if (count($loose_end_roots) > 0 && $missing_target_count > 0) {
252 $target_elements[] = $this->insertPathElementsAsScaffolds(
253 array_shift($loose_end_paths),
254 array_shift($loose_end_roots),
255 $path_conditions
256 );
257 }
258
259 // Move Navigator to path end
260 $navigator = $this->getLastNavigatorWithElements(
261 $path_conditions->getPathWithoutConditions(),
262 $set_root
263 );
264
265 // Move Navigator backwards to insert point: parent of first non unique element
266 $navigator = $this->moveNavigatorBackwardsToFirstNonUnique($navigator);
267 if ($navigator->hasPreviousStep()) {
268 $navigator = $navigator->previousStep();
269 }
270
271 // Add targets of loose ends
272 $missing_target_count = max(0, count($values) - count($target_elements));
273 $loose_end_paths = array_slice($loose_end_paths, 0, $missing_target_count);
274 $loose_end_roots = array_slice($loose_end_roots, 0, $missing_target_count);
275 array_push($target_elements, ...$this->createTargetElements($loose_end_roots, $loose_end_paths, $path_conditions));
276
277 // Add new targets
278 $missing_target_count = max(0, count($values) - count($target_elements));
279 $remaining_path = $this->remainingPathOfNavigator($navigator->nextStep());
280 $root = $navigator->lastElement();
281 $target_paths = array_fill(0, $missing_target_count, $remaining_path);
282 $target_roots = array_fill(0, $missing_target_count, $root);
283 array_push($target_elements, ...$this->createTargetElements($target_roots, $target_paths, $path_conditions));
284
285 return $target_elements;
286 }
287
292 {
293 while ($navigator->hasPreviousStep() && $navigator->lastElement()->getDefinition()->unique()) {
294 $navigator = $navigator->previousStep();
295 }
296 return $navigator;
297 }
298
300 {
301 $builder = $this->path_factory->custom()
302 ->withRelative(true)
303 ->withLeadsToExactlyOneElement(false);
304 while (!is_null($navigator)) {
305 if (is_null($navigator->currentStep())) {
306 $navigator = $navigator->nextStep();
307 continue;
308 }
309 $builder = $builder->withNextStepFromStep($navigator->currentStep(), false);
310 $navigator = $navigator->nextStep();
311 }
312 return $builder->get();
313 }
314
317 ElementInterface $root
319 $navigator = $this->navigator_factory->navigator($path, $root);
320 while ($navigator->hasNextStep() && $navigator->nextStep()->hasElements()) {
321 $navigator = $navigator->nextStep();
322 }
323 return $navigator;
324 }
325
331 ElementInterface $root,
332 PathConditionsCollectionInterface $path_conditions_collection
334 $navigator = $this->navigator_factory->navigator($path, $root);
335 $navigator = $navigator->nextStep();
336 $scaffold = $root;
337 while (!is_null($navigator)) {
338 $scaffold = $this->addAndMarkScaffoldByStep($scaffold, $navigator->currentStep());
339 if (is_null($scaffold)) {
340 throw new ilMDPathException(
341 'Cannot create scaffold at step: ' . $navigator->currentStep()->name() . ', invalid path.'
342 );
343 }
344 $condition_path = $path_conditions_collection->getConditionPathByStepName($navigator->currentStep()->name());
345 if ($condition_path->steps()->valid()) {
346 $this->insertConditionPathElementsAsScaffolds($condition_path, $scaffold);
347 }
348 $navigator = $navigator->nextStep();
349 }
350 return $scaffold;
351 }
352
359 protected function createTargetElements(
360 array $roots,
361 array $paths,
362 PathConditionsCollectionInterface $path_conditions
363 ): array {
364 $target_elements = [];
365 while (0 < count($paths)) {
366 $target_elements[] = $this->insertPathElementsAsScaffolds(
367 array_shift($paths),
368 array_shift($roots),
369 $path_conditions
370 );
371 }
372 return $target_elements;
373 }
374
379 PathInterface $condition_path,
380 ElementInterface $root
381 ): void {
382 $navigator = $this->navigator_factory->navigator($condition_path, $root);
383 $navigator = $navigator->nextStep();
384 $scaffold = $root;
385 while (!is_null($navigator)) {
386 $scaffold = $this->addAndMarkScaffoldByStep($scaffold, $navigator->currentStep());
387 if (is_null($scaffold)) {
388 throw new ilMDPathException('Cannot create scaffold, invalid path.');
389 }
390 $navigator = $navigator->nextStep();
391 }
392 }
393
397 protected function addAndMarkScaffoldByStep(
398 ElementInterface $element,
399 StepInterface $step
400 ): ?ElementInterface {
401 if ($step->name() === StepToken::SUPER) {
402 return $element->getSuperElement();
403 }
404 $scaffold = $element->addScaffoldToSubElements(
405 $this->scaffold_provider,
406 $step->name()
407 );
408 if (!isset($scaffold)) {
409 return null;
410 }
411 $data = '';
412 foreach ($step->filters() as $filter) {
413 if ($filter->type() === FilterType::DATA) {
414 $data = $filter->values()->current();
415 break;
416 }
417 }
418 $scaffold->mark(
419 $this->marker_factory,
420 Action::CREATE_OR_UPDATE,
421 $data
422 );
423 return $scaffold;
424 }
425
430 protected function markElementsCreateOrUpdate(array $target_elements, array $values): void
431 {
432 array_map(function (ElementInterface $element, ?string $value) {
433 $element->mark($this->marker_factory, Action::CREATE_OR_UPDATE, $value);
434 }, $target_elements, $values);
435 }
436
437 protected function markElementsDelete(array $target_elements): void
438 {
439 array_map(function (ElementInterface $element) {
440 $element->mark($this->marker_factory, Action::DELETE);
441 }, $target_elements);
442 }
443}
prepareCreateOrUpdate(SetInterface $set, PathInterface $path, string ... $values)
Definition: Manipulator.php:88
markElementsDelete(array $target_elements)
prepareForceCreate(SetInterface $set, PathInterface $path, string ... $values)
getLastNavigatorWithElements(PathInterface $path, ElementInterface $root)
markElementsCreateOrUpdate(array $target_elements, array $values)
PathUtilitiesFactoryInterface $path_utilities_factory
Definition: Manipulator.php:47
insertConditionPathElementsAsScaffolds(PathInterface $condition_path, ElementInterface $root)
__construct(ScaffoldProviderInterface $scaffold_provider, MarkerFactoryInterface $marker_factory, NavigatorFactoryInterface $navigator_factory, PathFactoryInterface $path_factory, PathUtilitiesFactoryInterface $path_utilities_factory)
Definition: Manipulator.php:49
ScaffoldProviderInterface $scaffold_provider
Definition: Manipulator.php:43
addAndMarkScaffoldByStep(ElementInterface $element, StepInterface $step)
also returns the added scaffold, if valid
insertPathElementsAsScaffolds(PathInterface $path, ElementInterface $root, PathConditionsCollectionInterface $path_conditions_collection)
createTargetElements(array $roots, array $paths, PathConditionsCollectionInterface $path_conditions)
NavigatorFactoryInterface $navigator_factory
Definition: Manipulator.php:45
remainingPathOfNavigator(NavigatorInterface $navigator)
MarkerFactoryInterface $marker_factory
Definition: Manipulator.php:44
moveNavigatorBackwardsToFirstNonUnique(NavigatorInterface $navigator)
mark(MarkerFactoryInterface $factory, Action $action, string $data_value='')
Leaves a trail of markers from this element up to the root element, or up to the first already marked...
prepareDelete(SetInterface $set, PathInterface $path)
nextStep()
Returns null if there is no next step.
lastElement()
Returns only the first element at the current step, if one exists.
$path
Definition: ltiservices.php:30
FilterType
Values should always be all lowercase.
Definition: FilterType.php:27
StepToken
The string representation of these tokens must not occur as names of metadata elements.
Definition: StepToken.php:28
if(!file_exists('../ilias.ini.php'))