ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
BasicPersistence.php
Go to the documentation of this file.
1 <?php
2 
20 
31 
32 class BasicPersistence implements Persistence
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 
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 }
$c
Definition: cli.php:38
const ANONYMOUS_USER_ID
Definition: constants.php:27
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
loadBucket(int $bucket_container_id)
Class ChatMainBarProvider .
getBucketIdsOfUser(int $user_id, string $order_by="id", string $order_direction="ASC")
[] Returns an array of bucket ids for the given user Id.
Class arConnector.
static where($where, $operator=null)
global $DIC
Definition: feed.php:28
updateBucket(Bucket $bucket)
Updates only the bucket! Use this if e.g.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getLastHeartbeat()
When was the last time that something happened on this bucket?
getBucketIdsByState(int $state)
int[] Returns a list of bucket ids for the given Observer State
saveBucketAndItsTasks(Bucket $bucket)
Fully updates or creates an Observer and all its tasks into the database.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Job.php:19
deleteBucket(Bucket $bucket)
Delete the bucket and all its stuff.
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$factory
Definition: metadata.php:75
deleteBucketById(int $bucket_id)
Deletes the Observer AND all its tasks and values.