ILIAS  release_8 Revision v8.24
BasicPersistence.php
Go to the documentation of this file.
1<?php
2
20
31
33{
34 protected static BasicPersistence $instance;
35 protected static array $buckets = [];
36 protected \ilDBInterface $db;
37 protected \SplObjectStorage $bucketHashToObserverContainerId;
38 protected \SplObjectStorage $taskHashToTaskContainerId;
39 protected \SplObjectStorage $valueHashToValueContainerId;
40 protected ?\arConnector $connector = null;
41 protected static array $tasks = [];
42
43 public static function instance(\ilDBInterface $db): \ILIAS\BackgroundTasks\Implementation\Persistence\BasicPersistence
44 {
45 if (!isset(self::$instance)) {
46 self::$instance = new BasicPersistence($db);
47 }
48
49 return self::$instance;
50 }
51
52 public function __construct(\ilDBInterface $db)
53 {
54 $this->db = $db;
55 $this->valueHashToValueContainerId = new \SplObjectStorage();
56 $this->bucketHashToObserverContainerId = new \SplObjectStorage();
57 $this->taskHashToTaskContainerId = new \SplObjectStorage();
58 }
59
60 protected function gc(): void
61 {
62 $this->db->manipulateF(
63 "DELETE FROM il_bt_bucket WHERE user_id = %s AND (state = %s OR state = %s)",
64 ['integer', 'integer', 'integer'],
65 [defined('ANONYMOUS_USER_ID') ? \ANONYMOUS_USER_ID : 13, State::FINISHED, State::USER_INTERACTION]
66 );
67 }
68
69 public function setConnector(\arConnector $c): void
70 {
71 $this->connector = $c;
72 }
73
78 public function saveBucketAndItsTasks(Bucket $bucket): void
79 {
80 $bucket->checkIntegrity();
81
82 $this->saveObserver($bucket);
83 }
84
88 public function updateBucket(Bucket $bucket): void
89 {
90 $bucketContainer = new BucketContainer($this->getBucketContainerId($bucket), $this->connector);
91
92 // The basic information about the task.
93 $bucketContainer->setUserId($bucket->getUserId());
94 $bucketContainer->setState($bucket->getState());
95 $bucketContainer->setTotalNumberoftasks(count($bucket->getTask()->unfoldTask()));
96 $bucketContainer->setPercentage($bucket->getOverallPercentage());
97 $bucketContainer->setTitle($bucket->getTitle());
98 $bucketContainer->setLastHeartbeat($bucket->getLastHeartbeat());
99 $bucketContainer->setDescription($bucket->getDescription());
100 $bucketContainer->setCurrentTaskid($this->getTaskContainerId($bucket->getCurrentTask()));
101 $bucketContainer->setRootTaskid($this->getTaskContainerId($bucket->getTask()));
102
103 // Save and store the container to bucket instance.
104 $bucketContainer->update();
105 }
106
110 public function getBucketIdsOfUser(int $user_id, string $order_by = "id", string $order_direction = "ASC"): array
111 {
112 // Garbage Collection
113 $this->gc();
114
115 return BucketContainer::where(['user_id' => $user_id])
116 ->orderBy($order_by, $order_direction)
117 ->getArray(null, 'id');
118 }
119
123 public function getBucketMetaOfUser(int $user_id): array
124 {
125 $buckets = BucketContainer::where(['user_id' => $user_id])->get();
126
127 return array_map(function (BucketContainer $bucketContainer): \ILIAS\BackgroundTasks\Implementation\Bucket\BasicBucketMeta {
128 $bucketMeta = new BasicBucketMeta();
129
130 $bucketMeta->setUserId($bucketContainer->getUserId());
131 $bucketMeta->setState($bucketContainer->getState());
132 $bucketMeta->setTitle($bucketContainer->getTitle());
133 $bucketMeta->setDescription($bucketContainer->getDescription());
134 $bucketMeta->setOverallPercentage($bucketContainer->getPercentage());
135
136 return $bucketMeta;
137 }, $buckets);
138 }
139
143 public function getBucketIdsByState(int $state): array
144 {
145 $buckets = BucketContainer::where(['state' => $state])->get();
146
147 return array_map(fn (BucketContainer $bucket_container): int => $bucket_container->getId(), $buckets);
148 }
149
154 protected function saveObserver(Bucket $bucket): void
155 {
156 // If the instance has a known container we use it, otherwise we create a new container.
157 if ($this->bucketHashToObserverContainerId->contains($bucket)) {
158 $bucketContainer = new BucketContainer($this->bucketHashToObserverContainerId[$bucket], $this->connector);
159 } else {
160 $bucketContainer = new BucketContainer(0, $this->connector);
161 }
162
163 // The basic information about the task.
164 $bucketContainer->setUserId($bucket->getUserId());
165 $bucketContainer->setState($bucket->getState());
166 $bucketContainer->setTitle($bucket->getTitle());
167 $bucketContainer->setDescription($bucket->getDescription());
168 $bucketContainer->setTotalNumberoftasks(count($bucket->getTask()->unfoldTask()));
169 $bucketContainer->setPercentage($bucket->getOverallPercentage());
170
171 // We want to store the bucket ID in every sub task and value. Thus we need to create an id if not available yet.
172 if (!$bucketContainer->getId()) {
173 $bucketContainer->create();
174 }
175
176 // The recursive part.
177 $this->saveTask($bucket->getTask(), $bucketContainer->getId());
178 if (!$bucket->hasCurrentTask()) {
179 $bucket->setCurrentTask($bucket->getTask());
180 }
181 $bucketContainer->setCurrentTaskid($this->getTaskContainerId($bucket->getCurrentTask()));
182 $bucketContainer->setRootTaskid($this->getTaskContainerId($bucket->getTask()));
183
184 // Save and store the container to bucket instance.
185 $bucketContainer->save();
186 $this->bucketHashToObserverContainerId[$bucket] = $bucketContainer->getId();
187 }
188
195 protected function saveTask(Task $task, int $bucketId): void
196 {
197 // If the instance has a known container we use it, otherwise we create a new container.
198 if ($this->taskHashToTaskContainerId->contains($task)) {
199 $taskContainer = new TaskContainer($this->taskHashToTaskContainerId[$task]);
200 } else {
201 $taskContainer = new TaskContainer(0);
202 }
203
204 // The basic information about the task.
205 $taskContainer->setType($task->getType());
206 $taskContainer->setBucketId($bucketId);
207 $reflection = new \ReflectionClass(get_class($task));
208 $taskContainer->setClassName(get_class($task));
209
210 // Recursivly save the inputs and link them to this task.
211 foreach ($task->getInput() as $k => $input) {
212 $this->saveValue($input, $bucketId, $k);
213 }
214 $this->saveValueToTask($task, $taskContainer, $bucketId);
215
216 // Save and store the container to the task instance.
217 $taskContainer->save();
218 $this->taskHashToTaskContainerId[$task] = $taskContainer->getId();
219 }
220
227 protected function saveValueToTask(Task $task, TaskContainer $taskContainer, int $bucketId): void
228 {
229 // If we have previous values to task associations we delete them.
230 if ($taskContainer->getId() !== 0) {
232 $olds = ValueToTaskContainer::where(['task_id' => $taskContainer->getId()])->get();
233 foreach ($olds as $old) {
234 $old->delete();
235 }
236 } else {
237 // We need a valid ID to link the inputs
238 $taskContainer->save();
239 }
240
241 // We create the new 1 to n relation.
242 foreach ($task->getInput() as $k => $inputValue) {
243 $v = new ValueToTaskContainer(0, $this->connector);
244 $v->setTaskId($taskContainer->getId());
245 $v->setPosition($k);
246 $v->setBucketId($bucketId);
247 $v->setValueId($this->getValueContainerId($inputValue));
248 $v->save();
249 }
250 }
251
258 protected function saveValue(Value $value, int $bucketId, int $position): void
259 {
260 // If we have previous values to task associations we delete them.
261 if ($this->valueHashToValueContainerId->contains($value)) {
262 $valueContainer = new ValueContainer($this->valueHashToValueContainerId[$value], $this->connector);
263 } else {
264 $valueContainer = new ValueContainer(0, $this->connector);
265 }
266 $valueContainer->setClassName(get_class($value));
267 // bugfix mantis 23503
268 // $absolute_class_path = $reflection->getFileName();
269 // $relative_class_path = str_replace(ILIAS_ABSOLUTE_PATH,".",$absolute_class_path);
270 // $valueContainer->setClassPath($relative_class_path);
271 $valueContainer->setType($value->getType());
272 $valueContainer->setHasParenttask($value->hasParentTask());
273 $valueContainer->setBucketId($bucketId);
274 $valueContainer->setPosition($position);
275 $valueContainer->setHash($value->getHash());
276 $valueContainer->setSerialized($value->serialize());
277
278 // If the value is a thunk value we also store its parent.
279 if ($value->hasParentTask()) {
280 $this->saveTask($value->getParentTask(), $bucketId);
281 $valueContainer->setParentTaskid($this->getTaskContainerId($value->getParentTask()));
282 }
283
284 // We save the container and store the instance to container association.
285 $valueContainer->save();
286 $this->valueHashToValueContainerId[$value] = $valueContainer->getId();
287 }
288
292 public function getBucketContainerId(Bucket $bucket): int
293 {
294 if (!$this->bucketHashToObserverContainerId->contains($bucket)) {
295 throw new SerializationException("Could not resolve container id of task: "
296 . print_r($bucket, true));
297 }
298
299 return (int) $this->bucketHashToObserverContainerId[$bucket];
300 }
301
306 protected function getTaskContainerId(Task $task): int
307 {
308 if (!$this->taskHashToTaskContainerId->contains($task)) {
309 throw new SerializationException("Could not resolve container id of task: "
310 . print_r($task, true));
311 }
312
313 return (int) $this->taskHashToTaskContainerId[$task];
314 }
315
319 protected function getValueContainerId(Value $value): int
320 {
321 if (!$this->valueHashToValueContainerId->contains($value)) {
322 throw new SerializationException("Could not resolve container id of value: "
323 . print_r($value, true));
324 }
325
326 return (int )$this->valueHashToValueContainerId[$value];
327 }
328
332 public function loadBucket(int $bucket_container_id): Bucket
333 {
334 if (isset(self::$buckets[$bucket_container_id])) {
335 return self::$buckets[$bucket_container_id];
336 }
338 $bucketContainer = BucketContainer::find($bucket_container_id);
339 if (!$bucketContainer) {
340 throw new BucketNotFoundException("The requested bucket with container id $bucket_container_id could not be found in the database.");
341 }
342 $bucket = new BasicBucket();
343
344 $bucket->setUserId($bucketContainer->getUserId());
345 $bucket->setState($bucketContainer->getState());
346 $bucket->setTitle($bucketContainer->getTitle());
347 $bucket->setDescription($bucketContainer->getDescription());
348 $bucket->setOverallPercentage($bucketContainer->getPercentage());
349 $bucket->setLastHeartbeat($bucketContainer->getLastHeartbeat());
350 $bucket->setTask($this->loadTask($bucketContainer->getRootTaskid(), $bucket, $bucketContainer));
351
352 $this->bucketHashToObserverContainerId[$bucket] = $bucket_container_id;
353
354 return $bucket;
355 }
356
365 private function loadTask(int $taskContainerId, Bucket $bucket, BucketContainer $bucketContainer): \ILIAS\BackgroundTasks\Task
366 {
367 global $DIC;
368 $factory = $DIC->backgroundTasks()->taskFactory();
370 $taskContainer = TaskContainer::find($taskContainerId);
373 $task = $factory->createTask($taskContainer->getClassName());
374
375 // Added additional orderBy for the id to ensure that the items are returned in the right order.
376 $valueToTasks = ValueToTaskContainer::where(['task_id' => $taskContainerId])
377 ->orderBy('task_id')
378 ->orderBy('position')
379 ->orderBy('id')
380 ->get();
381 $inputs = [];
382 foreach ($valueToTasks as $valueToTask) {
383 $inputs[] = $this->loadValue($valueToTask->getValueId(), $bucket, $bucketContainer);
384 }
385 $task->setInput($inputs);
386
387 if ($taskContainerId === $bucketContainer->getCurrentTaskid()) {
388 $bucket->setCurrentTask($task);
389 }
390
391 $this->taskHashToTaskContainerId[$task] = $taskContainerId;
392
393 return $task;
394 }
395
396 private function loadValue($valueContainerId, Bucket $bucket, BucketContainer $bucketContainer): \ILIAS\BackgroundTasks\Value
397 {
398 global $DIC;
399 $factory = $DIC->backgroundTasks()->injector();
400
402 $valueContainer = ValueContainer::find($valueContainerId);
406 $value = $factory->createInstance($valueContainer->getClassName());
407
408 $value->unserialize($valueContainer->getSerialized());
409 if ($valueContainer->getHasParenttask() !== 0) {
410 $value->setParentTask($this->loadTask($valueContainer->getParentTaskid(), $bucket, $bucketContainer));
411 }
412
413 $this->valueHashToValueContainerId[$value] = $valueContainerId;
414
415 return $value;
416 }
417
418 public function deleteBucketById(int $bucket_id): void
419 {
420 $buckets = BucketContainer::where(['id' => $bucket_id])->get();
421 array_map(function (BucketContainer $item): void {
422 $item->delete();
423 }, $buckets);
424
426 $tasks = TaskContainer::where(['bucket_id' => $bucket_id])->get();
427 array_map(function (TaskContainer $item): void {
428 $item->delete();
429 }, $tasks);
430
432 $values = ValueContainer::where(['bucket_id' => $bucket_id])->get();
433 array_map(function (ValueContainer $item): void {
434 $item->delete();
435 }, $values);
436
438 $valueToTasks = ValueToTaskContainer::where(['bucket_id' => $bucket_id])->get();
439 array_map(function (ValueToTaskContainer $item): void {
440 $item->delete();
441 }, $valueToTasks);
442 }
443
447 public function deleteBucket(Bucket $bucket): void
448 {
449 $id = $this->getBucketContainerId($bucket);
450 $this->deleteBucketById($id);
451 $this->bucketHashToObserverContainerId->detach($bucket);
452 }
453
457 public function loadBuckets(array $bucket_container_ids): array
458 {
459 $buckets = [];
460 foreach ($bucket_container_ids as $bucket_id) {
461 try {
462 $buckets[] = $this->loadBucket($bucket_id);
463 } catch (\Throwable $t) {
464 // there seem to be a problem with this container, we must delete it
465 $this->deleteBucketById($bucket_id);
466 }
467 }
468
469 return $buckets;
470 }
471}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
static where($where, $operator=null)
saveBucketAndItsTasks(Bucket $bucket)
Fully updates or creates an Observer and all its tasks into the database.
getBucketIdsOfUser(int $user_id, string $order_by="id", string $order_direction="ASC")
\int[] Returns an array of bucket ids for the given user Id.
updateBucket(Bucket $bucket)
Updates only the bucket! Use this if e.g.
deleteBucket(Bucket $bucket)
Delete the bucket and all its stuff.
getBucketIdsByState(int $state)
int[] Returns a list of bucket ids for the given Observer State
Class arConnector.
$c
Definition: cli.php:38
const ANONYMOUS_USER_ID
Definition: constants.php:27
global $DIC
Definition: feed.php:28
getLastHeartbeat()
When was the last time that something happened on this bucket?
loadBucket(int $bucket_container_id)
deleteBucketById(int $bucket_id)
Deletes the Observer AND all its tasks and values.
Interface ilDBInterface.
$factory
Definition: metadata.php:75
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Job.php:19
Class ChatMainBarProvider \MainMenu\Provider.