ILIAS  release_7 Revision v7.30-3-g800a261c036
BasicPersistence.php
Go to the documentation of this file.
1<?php
2
20
31
33{
34
38 protected static $instance;
42 protected static $buckets = [];
58 protected static $tasks = [];
62 protected $connector = null;
63
64
65 public static function instance(\ilDBInterface $db) : \ILIAS\BackgroundTasks\Implementation\Persistence\BasicPersistence
66 {
67 if (!isset(self::$instance)) {
68 self::$instance = new BasicPersistence($db);
69 }
70
71 return self::$instance;
72 }
73 public function __construct(\ilDBInterface $db)
74 {
75 $this->db = $db;
76 $this->valueHashToValueContainerId = new \SplObjectStorage();
77 $this->bucketHashToObserverContainerId = new \SplObjectStorage();
78 $this->taskHashToTaskContainerId = new \SplObjectStorage();
79 }
80
81 protected function gc() : void
82 {
83 $this->db->manipulateF(
84 "DELETE FROM il_bt_bucket WHERE user_id = %s AND (state = %s OR state = %s)",
85 ['integer', 'integer', 'integer'],
86 [defined('ANONYMOUS_USER_ID') ? \ANONYMOUS_USER_ID : 13, State::FINISHED, State::USER_INTERACTION]
87 );
88 }
89
90 public function setConnector(\arConnector $c) : void
91 {
92 $this->connector = $c;
93 }
94
95
101 public function saveBucketAndItsTasks(Bucket $bucket)
102 {
103 $bucket->checkIntegrity();
104
105 $this->saveObserver($bucket);
106 }
107
108
114 public function updateBucket(Bucket $bucket)
115 {
116 $bucketContainer = new BucketContainer($this->getBucketContainerId($bucket), $this->connector);
117
118 // The basic information about the task.
119 $bucketContainer->setUserId($bucket->getUserId());
120 $bucketContainer->setState($bucket->getState());
121 $bucketContainer->setTotalNumberoftasks(count($bucket->getTask()->unfoldTask()));
122 $bucketContainer->setPercentage($bucket->getOverallPercentage());
123 $bucketContainer->setTitle($bucket->getTitle());
124 $bucketContainer->setLastHeartbeat($bucket->getLastHeartbeat());
125 $bucketContainer->setDescription($bucket->getDescription());
126 $bucketContainer->setCurrentTaskid($this->getTaskContainerId($bucket->getCurrentTask()));
127 $bucketContainer->setRootTaskid($this->getTaskContainerId($bucket->getTask()));
128
129 // Save and store the container to bucket instance.
130 $bucketContainer->update();
131 }
132
133
137 public function getBucketIdsOfUser($user_id, $order_by = "id", $order_direction = "ASC")
138 {
139 // Garbage Collection
140 $this->gc();
141
142 return BucketContainer::where(['user_id' => $user_id])
143 ->orderBy($order_by, $order_direction)
144 ->getArray(null, 'id');
145 }
146
147
153 public function getBucketMetaOfUser($user_id)
154 {
155 $buckets = BucketContainer::where(['user_id' => $user_id])->get();
156 $bucketMetas = array_map(function (BucketContainer $bucketContainer) {
157 $bucketMeta = new BasicBucketMeta();
158
159 $bucketMeta->setUserId($bucketContainer->getUserId());
160 $bucketMeta->setState($bucketContainer->getState());
161 $bucketMeta->setTitle($bucketContainer->getTitle());
162 $bucketMeta->setDescription($bucketContainer->getDescription());
163 $bucketMeta->setOverallPercentage($bucketContainer->getPercentage());
164
165 return $bucketMeta;
166 }, $buckets);
167
168 return $bucketMetas;
169 }
170
171
175 public function getBucketIdsByState($state)
176 {
177 $buckets = BucketContainer::where(['state' => $state])->get();
178 $ids = array_map(function (BucketContainer $bucket_container) {
179 return $bucket_container->getId();
180 }, $buckets);
181
182 return $ids;
183 }
184
185
192 protected function saveObserver(Bucket $bucket)
193 {
194 // If the instance has a known container we use it, otherwise we create a new container.
195 if ($this->bucketHashToObserverContainerId->contains($bucket)) {
196 $bucketContainer = new BucketContainer($this->bucketHashToObserverContainerId[$bucket], $this->connector);
197 } else {
198 $bucketContainer = new BucketContainer(0, $this->connector);
199 }
200
201 // The basic information about the task.
202 $bucketContainer->setUserId($bucket->getUserId());
203 $bucketContainer->setState($bucket->getState());
204 $bucketContainer->setTitle($bucket->getTitle());
205 $bucketContainer->setDescription($bucket->getDescription());
206 $bucketContainer->setTotalNumberoftasks(count($bucket->getTask()->unfoldTask()));
207 $bucketContainer->setPercentage($bucket->getOverallPercentage());
208
209 // We want to store the bucket ID in every sub task and value. Thus we need to create an id if not available yet.
210 if (!$bucketContainer->getId()) {
211 $bucketContainer->create();
212 }
213
214 // The recursive part.
215 $this->saveTask($bucket->getTask(), $bucketContainer->getId());
216 if (!$bucket->getCurrentTask()) {
217 $bucket->setCurrentTask($bucket->getTask());
218 }
219 $bucketContainer->setCurrentTaskid($this->getTaskContainerId($bucket->getCurrentTask()));
220 $bucketContainer->setRootTaskid($this->getTaskContainerId($bucket->getTask()));
221
222 // Save and store the container to bucket instance.
223 $bucketContainer->save();
224 $this->bucketHashToObserverContainerId[$bucket] = $bucketContainer->getId();
225 }
226
227
235 protected function saveTask(Task $task, $bucketId)
236 {
237 // If the instance has a known container we use it, otherwise we create a new container.
238 if ($this->taskHashToTaskContainerId->contains($task)) {
239 $taskContainer = new TaskContainer($this->taskHashToTaskContainerId[$task], $this->connector);
240 } else {
241 $taskContainer = new TaskContainer(0, $this->connector);
242 }
243
244 // The basic information about the task.
245 $taskContainer->setType($task->getType());
246 $taskContainer->setBucketId($bucketId);
247 $reflection = new \ReflectionClass(get_class($task));
248 $taskContainer->setClassName(get_class($task));
249 // bugfix mantis 23503
250 $absolute_class_path = $reflection->getFileName();
251 // $relative_class_path = str_replace(ILIAS_ABSOLUTE_PATH,".",$absolute_class_path);
252 $taskContainer->setClassPath($reflection->getFileName());
253
254 // Recursivly save the inputs and link them to this task.
255 foreach ($task->getInput() as $k => $input) {
256 $this->saveValue($input, $bucketId, $k);
257 }
258 $this->saveValueToTask($task, $taskContainer, $bucketId);
259
260 // Save and store the container to the task instance.
261 $taskContainer->save();
262 $this->taskHashToTaskContainerId[$task] = $taskContainer->getId();
263 }
264
265
274 protected function saveValueToTask(Task $task, TaskContainer $taskContainer, $bucketId)
275 {
276 // If we have previous values to task associations we delete them.
277 if ($taskContainer->getId()) {
279 $olds = ValueToTaskContainer::where(['task_id' => $taskContainer->getId()])->get();
280 foreach ($olds as $old) {
281 $old->delete();
282 }
283 } else {
284 // We need a valid ID to link the inputs
285 $taskContainer->save();
286 }
287
288 // We create the new 1 to n relation.
289 foreach ($task->getInput() as $k => $inputValue) {
290 $v = new ValueToTaskContainer(0, $this->connector);
291 $v->setTaskId($taskContainer->getId());
292 $v->setPosition($k);
293 $v->setBucketId($bucketId);
294 $v->setValueId($this->getValueContainerId($inputValue));
295 $v->save();
296 }
297 }
298
299
307 protected function saveValue(Value $value, $bucketId, $position)
308 {
309 // If we have previous values to task associations we delete them.
310 if ($this->valueHashToValueContainerId->contains($value)) {
311 $valueContainer = new ValueContainer($this->valueHashToValueContainerId[$value], $this->connector);
312 } else {
313 $valueContainer = new ValueContainer(0, $this->connector);
314 }
315
316 // Save information about the value
317 $reflection = new \ReflectionClass(get_class($value));
318 $valueContainer->setClassName(get_class($value));
319 // bugfix mantis 23503
320 // $absolute_class_path = $reflection->getFileName();
321 // $relative_class_path = str_replace(ILIAS_ABSOLUTE_PATH,".",$absolute_class_path);
322 // $valueContainer->setClassPath($relative_class_path);
323 $valueContainer->setType($value->getType());
324 $valueContainer->setHasParenttask($value->hasParentTask());
325 $valueContainer->setBucketId($bucketId);
326 $valueContainer->setPosition($position);
327 $valueContainer->setHash($value->getHash());
328 $valueContainer->setSerialized($value->serialize());
329
330 // If the value is a thunk value we also store its parent.
331 if ($value->hasParentTask()) {
332 $this->saveTask($value->getParentTask(), $bucketId);
333 $valueContainer->setParentTaskid($this->getTaskContainerId($value->getParentTask()));
334 }
335
336 // We save the container and store the instance to container association.
337 $valueContainer->save();
338 $this->valueHashToValueContainerId->attach($value, $valueContainer->getId());
339 }
340
341
348 public function getBucketContainerId(Bucket $bucket)
349 {
350 if (!$this->bucketHashToObserverContainerId->contains(($bucket))) {
351 throw new SerializationException("Could not resolve container id of task: "
352 . print_r($bucket, true));
353 }
354
355 return $this->bucketHashToObserverContainerId[$bucket];
356 }
357
358
365 protected function getTaskContainerId(Task $task)
366 {
367 if (!$this->taskHashToTaskContainerId->contains($task)) {
368 throw new SerializationException("Could not resolve container id of task: "
369 . print_r($task, true));
370 }
371
372 return $this->taskHashToTaskContainerId[$task];
373 }
374
375
382 protected function getValueContainerId($value) : int
383 {
384 if (!$this->valueHashToValueContainerId->contains($value)) {
385 throw new SerializationException("Could not resolve container id of value: "
386 . print_r($value, true));
387 }
388
389 return (int) $this->valueHashToValueContainerId[$value];
390 }
391
392
399 public function loadBucket($bucket_id)
400 {
401 if (isset(self::$buckets[$bucket_id])) {
402 return self::$buckets[$bucket_id];
403 }
405 $bucketContainer = BucketContainer::find($bucket_id);
406 if (!$bucketContainer) {
407 throw new BucketNotFoundException("The requested bucket with container id $bucket_id could not be found in the database.");
408 }
409 $bucket = new BasicBucket();
410
411 $bucket->setUserId($bucketContainer->getUserId());
412 $bucket->setState($bucketContainer->getState());
413 $bucket->setTitle($bucketContainer->getTitle());
414 $bucket->setDescription($bucketContainer->getDescription());
415 $bucket->setOverallPercentage($bucketContainer->getPercentage());
416 $bucket->setLastHeartbeat($bucketContainer->getLastHeartbeat());
417 $bucket->setTask($this->loadTask($bucketContainer->getRootTaskid(), $bucket, $bucketContainer));
418
419 $this->bucketHashToObserverContainerId[$bucket] = $bucket_id;
420
421 return $bucket;
422 }
423
424
436 private function loadTask($taskContainerId, Bucket $bucket, BucketContainer $bucketContainer)
437 {
438 global $DIC;
439 $factory = $DIC->backgroundTasks()->taskFactory();
441 $taskContainer = TaskContainer::find($taskContainerId);
444 $task = $factory->createTask($taskContainer->getClassName());
445
446 // Bugfix 0023775
447 // Added additional orderBy for the id to ensure that the items are returned in the right order.
448
450 $valueToTasks = ValueToTaskContainer::where(['task_id' => $taskContainerId])
451 ->orderBy('task_id')
452 ->orderBy('position')
453 ->orderBy('id')
454 ->get();
455 $inputs = [];
456 foreach ($valueToTasks as $valueToTask) {
457 $inputs[] = $this->loadValue($valueToTask->getValueId(), $bucket, $bucketContainer);
458 }
459 $task->setInput($inputs);
460
461 if ($taskContainerId == $bucketContainer->getCurrentTaskid()) {
462 $bucket->setCurrentTask($task);
463 }
464
465 $this->taskHashToTaskContainerId[$task] = $taskContainerId;
466
467 return $task;
468 }
469
470
471 private function loadValue($valueContainerId, Bucket $bucket, BucketContainer $bucketContainer)
472 {
473 global $DIC;
474 $factory = $DIC->backgroundTasks()->injector();
475
477 $valueContainer = ValueContainer::find($valueContainerId);
481 $value = $factory->createInstance($valueContainer->getClassName());
482
483 $value->unserialize($valueContainer->getSerialized());
484 if ($valueContainer->getHasParenttask()) {
485 $value->setParentTask($this->loadTask($valueContainer->getParentTaskid(), $bucket, $bucketContainer));
486 }
487
488 $this->valueHashToValueContainerId[$value] = $valueContainerId;
489
490 return $value;
491 }
492
493
494 public function deleteBucketById($bucket_id)
495 {
496// return;
498 $buckets = BucketContainer::where(['id' => $bucket_id])->get();
499 array_map(function (\ActiveRecord $item) {
500 $item->delete();
501 }, $buckets);
502
504 $tasks = TaskContainer::where(['bucket_id' => $bucket_id])->get();
505 array_map(function (\ActiveRecord $item) {
506 $item->delete();
507 }, $tasks);
508
510 $values = ValueContainer::where(['bucket_id' => $bucket_id])->get();
511 array_map(function (\ActiveRecord $item) {
512 $item->delete();
513 }, $values);
514
516 $valueToTasks = ValueToTaskContainer::where(['bucket_id' => $bucket_id])->get();
517 array_map(function (\ActiveRecord $item) {
518 $item->delete();
519 }, $valueToTasks);
520 }
521
522
526 public function deleteBucket($bucket)
527 {
528 $id = $this->getBucketContainerId($bucket);
529 $this->deleteBucketById($id);
530 $this->bucketHashToObserverContainerId->detach($bucket);
531 }
532
533
539 public function loadBuckets($bucket_container_id)
540 {
541 $buckets = [];
542 foreach ($bucket_container_id as $bucket_id) {
543 try {
544 $buckets[] = $this->loadBucket($bucket_id);
545 } catch (\Throwable $t) {
546 // there seem to be a problem with this container, we must delete it
547 $this->deleteBucketById($bucket_id);
548 }
549 }
550
551 return $buckets;
552 }
553}
Class ActiveRecord.
static where($where, $operator=null)
An exception for terminatinating execution or to throw for unit testing.
saveBucketAndItsTasks(Bucket $bucket)
Fully updates or creates an Observer and all its tasks into the database.
updateBucket(Bucket $bucket)
Updates only the bucket! Use this if e.g.
getBucketIdsOfUser($user_id, $order_by="id", $order_direction="ASC")
\int[] Returns an array of bucket ids for the given user Id.
getBucketIdsByState($state)
int[] Returns a list of bucket ids for the given Observer State
deleteBucket($bucket)
Delete the bucket and all its stuff.void
Class arConnector.
$c
Definition: cli.php:37
const ANONYMOUS_USER_ID
Definition: constants.php:25
global $DIC
Definition: goto.php:24
getLastHeartbeat()
When was the last time that something happened on this bucket?
deleteBucketById($bucket_id)
Deletes the Observer AND all its tasks and values.
loadBucket($bucket_container_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$factory
Definition: metadata.php:58
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ChatMainBarProvider \MainMenu\Provider.