ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
DatabaseDocumentRepository.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
23use DateTimeImmutable;
39
41{
42 use DeriveFieldTypes;
43
44 public function __construct(
45 private readonly string $id,
46 private readonly ilDBInterface $database,
47 private readonly UserAction $action,
48 ) {
49 }
50
51 public function createDocument(string $title, DocumentContent $content): void
52 {
53 $creation = $this->action->modifiedNow();
54 $this->insert($this->documentTable(), [
55 'title' => $title,
56 'creation_ts' => $creation->time(),
57 'owner_usr_id' => $creation->user(),
58 'text' => $content->value(),
59 'type' => $content->type(),
60 'sorting' => $this->nextSorting(),
61 'provider' => $this->id,
62 ]);
63 }
64
65 public function createCriterion(Document $document, CriterionContent $content): void
66 {
67 $creation = $this->action->modifiedNow();
68 $this->insert($this->criterionTable(), [
69 'doc_id' => $document->id(),
70 'assigned_ts' => $creation->time(),
71 'owner_usr_id' => $creation->user(),
72 ...$this->criterionFields($content),
73 ]);
74 }
75
76 public function deleteDocument(Document $document): void
77 {
78 $this->deleteEntry($this->documentTable(), $document->id(), 'id', true);
79 }
80
81 public function deleteCriterion(int $criterion_id): void
82 {
83 $this->deleteEntry($this->criterionTable(), $criterion_id, 'doc_id');
84 }
85
86 public function updateDocumentTitle(DocumentId $document_id, string $title): void
87 {
88 $this->updateDocument($document_id, ['title' => $title]);
89 }
90
91 public function updateDocumentContent(DocumentId $document_id, DocumentContent $content): void
92 {
93 $this->updateDocument($document_id, ['text' => $content->value(), 'type' => $content->type()]);
94 }
95
96 public function updateDocumentOrder(DocumentId $document_id, int $order): void
97 {
98 $this->updateDocument($document_id, ['sorting' => $order], true);
99 }
100
101 public function updateCriterionContent(int $criterion_id, CriterionContent $content): void
102 {
103 $modification = $this->action->modifiedNow();
104
105 $this->database->update($this->criterionTable(), $this->deriveFieldTypes([
106 'modification_ts' => $modification->time(),
107 'last_modified_usr_id' => $modification->user(),
108 ...$this->criterionFields($content),
109 ]), $this->deriveFieldTypes([
110 'id' => $criterion_id,
111 ]));
112 }
113
117 private function updateDocument(DocumentId $document_id, array $fields_and_values, bool $silent = false): void
118 {
119 match ($document_id::class) {
120 HashId::class => $this->lazyDocFields($fields_and_values, $document_id->hash(), $silent),
121 NumberId::class => $this->setDocFields($fields_and_values, $document_id->number(), $silent),
122 };
123 }
124
125 public function countAll(): int
126 {
127 return (int) current($this->queryF('SELECT COUNT(1) as c FROM ' . $this->documentTable() . ' WHERE provider = %s', [$this->id]))['c'];
128 }
129
133 public function all(int $offset = 0, ?int $limit = null): array
134 {
135 return $this->queryDocuments('1', $limit === null ? '' : ' LIMIT ' . $offset . ', ' . $limit);
136 }
137
142 public function select(array $ids): array
143 {
144 if ([] === $ids) {
145 return [];
146 }
147 return $this->queryDocuments($this->database->in('id', $ids, false, ilDBConstants::T_INTEGER));
148 }
149
153 public function find(int $id): Result
154 {
155 return $this->first(
156 $this->select([$id]),
157 'Document with ID ' . $id . ' not found.'
158 );
159 }
160
161 public function findId(DocumentId $document_id): Result
162 {
163 return match ($document_id::class) {
164 HashId::class => $this->findHash($document_id->hash()),
165 NumberId::class => $this->find($document_id->number()),
166 };
167 }
168
183 public function documentFromRow(array $row, array $criteria): Document
184 {
185 return new Document((int) $row['id'], new Meta(
186 (int) $row['sorting'],
187 new Edit((int) $row['last_modified_usr_id'], new DateTimeImmutable('@' . $row['modification_ts'])),
188 new Edit((int) $row['owner_usr_id'], new DateTimeImmutable('@' . $row['creation_ts']))
189 ), new DocumentContent($row['type'], $row['title'] ?? '', $row['text'] ?? ''), $criteria);
190 }
191
192 public function documentTable(): string
193 {
194 return 'ldoc_documents';
195 }
196
197 public function exists(string $doc_id_name): string
198 {
199 $documents = $this->documentTable();
200 $provider = $this->database->quote($this->id, ilDBConstants::T_TEXT);
201 $table = 't' . random_int(0, 100);
202 return "EXISTS (SELECT 1 FROM $documents AS $table WHERE $table.id = $doc_id_name AND $table.provider = $provider)";
203 }
204
208 private function setDocFields(array $fields_and_values, int $doc_id, bool $silent): void
209 {
210 $modification = $this->action->modifiedNow();
211 $this->database->update($this->documentTable(), $this->deriveFieldTypes([
212 ...$fields_and_values,
213 ...($silent ? [] : [
214 'modification_ts' => $modification->time(),
215 'last_modified_usr_id' => $modification->user(),
216 ]),
217 ]), $this->deriveFieldTypes([
218 'id' => $doc_id,
219 'provider' => $this->id,
220 ]));
221 }
222
226 private function lazyDocFields(array $fields_and_values, string $hash, bool $silent): void
227 {
228 $modification = $this->action->modifiedNow();
229 $affected_rows = $this->database->update($this->documentTable(), $this->deriveFieldTypes([
230 ...$fields_and_values,
231 ...($silent ? [] : [
232 'modification_ts' => $modification->time(),
233 'last_modified_usr_id' => $modification->user(),
234 ]),
235 ]), $this->deriveFieldTypes([
236 'hash' => $hash,
237 'provider' => $this->id,
238 ]));
239
240 if (0 === $affected_rows) {
241 $this->database->insert($this->documentTable(), $this->deriveFieldTypes([
242 'id' => $this->database->nextId($this->documentTable()),
243 'creation_ts' => $modification->time(),
244 'owner_usr_id' => $modification->user(),
245 'modification_ts' => $modification->time(),
246 'last_modified_usr_id' => $modification->user(),
247 'sorting' => $this->nextSorting(),
248 'provider' => $this->id,
249 'title' => 'Unnamed document',
250 'hash' => $hash,
251 ...$fields_and_values,
252 ]));
253 }
254 }
255
259 private function criterionFields(CriterionContent $content): array
260 {
261 return [
262 'criterion_id' => $content->type(),
263 'criterion_value' => json_encode($content->arguments()),
264 ];
265 }
266
270 private function queryDocuments(string $where = '1', string $limit = ''): array
271 {
272 $doc_table = $this->documentTable();
273 $provider = $this->database->quote($this->id, ilDBConstants::T_TEXT);
274 $documents = $this->query('SELECT * FROM ' . $doc_table . ' WHERE ' . $where . ' AND provider = ' . $provider . ' ORDER BY sorting ' . $limit);
275 $doc_ids = array_map(fn($doc) => (int) $doc['id'], $documents);
276 $array = $this->query(join(' ', [
277 'SELECT * FROM',
278 $this->criterionTable(),
279 'WHERE',
280 $this->database->in('doc_id', $doc_ids, false, ilDBConstants::T_INTEGER),
281 'AND',
282 $this->exists('doc_id')
283 ]));
284
285 $assignments = [];
286 foreach ($array as $row) {
287 $document_id = (int) $row['doc_id'];
288 $assignments[$document_id] ??= [];
289 $assignments[$document_id][] = $this->criterionFromRow($row);
290 }
291
292
293 return array_map(
294 fn($doc) => $this->documentFromRow($doc, $assignments[(int) $doc['id']] ?? []),
295 $documents
296 );
297 }
298
299 private function criterionFromRow(array $row): Criterion
300 {
301 return new Criterion(
302 (int) $row['id'],
304 $row['criterion_id'],
305 json_decode($row['criterion_value'], true)
306 ),
307 new Edit((int) $row['last_modified_usr_id'], new DateTimeImmutable('@' . $row['modification_ts'])),
308 new Edit((int) $row['owner_usr_id'], new DateTimeImmutable('@' . $row['assigned_ts']))
309 );
310 }
311
312 private function criterionTable(): string
313 {
314 return 'ldoc_criteria';
315 }
316
320 private function insert(string $table, array $fields_and_values): void
321 {
322 $id = $this->database->nextId($table);
323
324 $this->database->insert($table, $this->deriveFieldTypes([...$fields_and_values, 'id' => $id]));
325 }
326
330 private function update(int $id, string $table, array $fields_and_values): void
331 {
332 $this->database->update($table, $this->deriveFieldTypes($fields_and_values), $this->deriveFieldTypes([
333 'id' => $id,
334 ]));
335 }
336
337 private function deleteEntry(string $table, int $id, string $doc_field, bool $cleanup = false): void
338 {
339 $id = $this->database->quote($id, ilDBConstants::T_INTEGER);
340 $this->database->manipulate("DELETE FROM $table WHERE id = $id AND " . $this->exists($table . '.' . $doc_field));
341 if ($cleanup) {
342 $this->cleanupCriteria();
343 }
344 }
345
346 private function cleanupCriteria(): void
347 {
348 $criteria = $this->criterionTable();
349 $documents = $this->documentTable();
350 // The provider is not specified because this statement cleans up dead criteria independent of the provider.
351 $this->database->manipulate("DELETE FROM $criteria WHERE doc_id NOT IN (SELECT id FROM $documents)");
352 }
353
354 private function nextSorting(): int
355 {
356 $documents = $this->documentTable();
357 $sorting = (int) ($this->database->fetchAssoc($this->database->query(
358 "SELECT MAX(sorting) as s FROM $documents WHERE " . $this->exists($documents . '.id')
359 ))['s'] ?? 0);
360
361 return $sorting + 10;
362 }
363
364 private function findHash(string $hash): Result
365 {
366 return $this->first(
367 $this->queryDocuments($this->database->in('hash', [$hash], false, ilDBConstants::T_TEXT)),
368 'Document with hash . ' . json_encode($hash) . ' not found.'
369 );
370 }
371
372 private function first(array $array, string $message): Result
373 {
374 $document = current($array) ?: null;
375 return $document ? new Ok($document) : new Error($message);
376 }
377}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
A result encapsulates a value or an error and simplifies the handling of those.
Definition: Ok.php:31
deleteEntry(string $table, int $id, string $doc_field, bool $cleanup=false)
update(int $id, string $table, array $fields_and_values)
__construct(private readonly string $id, private readonly ilDBInterface $database, private readonly UserAction $action,)
createCriterion(Document $document, CriterionContent $content)
lazyDocFields(array $fields_and_values, string $hash, bool $silent)
setDocFields(array $fields_and_values, int $doc_id, bool $silent)
updateDocumentContent(DocumentId $document_id, DocumentContent $content)
updateDocument(DocumentId $document_id, array $fields_and_values, bool $silent=false)
updateCriterionContent(int $criterion_id, CriterionContent $content)
Class ilDBConstants.
A result encapsulates a value or an error and simplifies the handling of those.
Definition: Result.php:29
Interface ilDBInterface.
$provider
Definition: ltitoken.php:80