ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilSearchResult.php
Go to the documentation of this file.
1 <?php
2 
27 {
28  private string $permission = 'visible';
29 
30  private int $user_id;
31  private array $entries = array();
32  private array $results = array();
33  private array $observers = array();
34  private int $max_hits = 0;
35 
37  protected int $offset = 0;
38 
39  // OBJECT VARIABLES
40  protected ilAccess $ilAccess;
41  protected ilDBInterface $db;
42  protected ilTree $tree;
43  protected ilObjUser $user;
45 
46  // Stores info if MAX HITS is reached or not
47  public bool $limit_reached = false;
48 
49  protected bool $preventOverwritingMaxhits = false;
50 
51  protected ilLogger $logger;
52 
53  public function __construct(int $a_user_id = 0)
54  {
55  global $DIC;
56 
57  $this->logger = $DIC->logger()->src();
58  $this->ilAccess = $DIC->access();
59  $this->db = $DIC->database();
60  $this->tree = $DIC->repositoryTree();
61  $this->user = $DIC->user();
62 
63  if ($a_user_id) {
64  $this->user_id = $a_user_id;
65  } else {
66  $this->user_id = $this->user->getId();
67  }
69  $this->initUserSearchCache();
70  }
71 
75  public function setRequiredPermission(string $a_permission): void
76  {
77  $this->permission = $a_permission;
78  }
79 
80  public function getRequiredPermission(): string
81  {
82  return $this->permission;
83  }
84 
85  public function setUserId(int $a_user_id): void
86  {
87  $this->user_id = $a_user_id;
88  }
89  public function getUserId(): int
90  {
91  return $this->user_id;
92  }
93 
94  public function getEntries(): array
95  {
96  return $this->entries;
97  }
98 
99  public function isLimitReached(): bool
100  {
101  return $this->limit_reached;
102  }
103 
104  public function setMaxHits(int $a_max_hits): void
105  {
106  $this->max_hits = $a_max_hits;
107  }
108  public function getMaxHits(): int
109  {
110  return $this->max_hits;
111  }
112 
116  public function isOffsetReached(int $a_counter): bool
117  {
118  return !($a_counter < $this->offset);
119  }
120 
131  public function addEntry(int $a_obj_id, string $a_type, array $found, int $a_child_id = 0): void
132  {
133  // Create new entry if it not exists
134  if (!isset($this->entries[$a_obj_id])) {
135  $this->entries[$a_obj_id]['obj_id'] = $a_obj_id;
136  $this->entries[$a_obj_id]['type'] = $a_type;
137  $this->entries[$a_obj_id]['found'] = $found;
138  $this->entries[$a_obj_id]['child'] = [];
139 
140  if ($a_child_id and $a_child_id != $a_obj_id) {
141  $this->entries[$a_obj_id]['child'][$a_child_id] = $a_child_id;
142  }
143  } else {
144  // replace or add child ('pg','st') id
145  if ($a_child_id and $a_child_id != $a_obj_id) {
146  $this->entries[$a_obj_id]['child'][$a_child_id] = $a_child_id;
147  }
148  $counter = 0;
149  foreach ($found as $position) {
150  if ($position) {
151  $this->entries[$a_obj_id]['found'][$counter] = $position;
152  }
153  $counter++;
154  }
155  }
156  }
157 
158  public function numEntries(): int
159  {
160  return count($this->getEntries());
161  }
162 
167  public function mergeEntries(ilSearchResult $result_obj): void
168  {
169  foreach ($result_obj->getEntries() as $obj_id => $entry) {
170  $this->addEntry($entry['obj_id'], $entry['type'], $entry['found']);
171  $this->__updateEntryChilds($entry['obj_id'], $entry['child']);
172  }
173  }
174 
179  public function diffEntriesFromResult(): void
180  {
181  $new_entries = $this->getEntries();
182  $this->entries = array();
183 
184  // Get all checked objects
185  foreach ($this->search_cache->getCheckedItems() as $ref_id => $obj_id) {
186  if (isset($new_entries[$obj_id])) {
187  $this->addEntry(
188  $new_entries[$obj_id]['obj_id'],
189  $new_entries[$obj_id]['type'],
190  $new_entries[$obj_id]['found']
191  );
192  $this->__updateEntryChilds(
193  $new_entries[$obj_id]['obj_id'],
194  $new_entries[$obj_id]['child']
195  );
196  }
197  }
198  }
199 
203  public function intersectEntries(ilSearchResult $result_obj): void
204  {
205  $new_entries = $this->getEntries();
206  $this->entries = [];
207  foreach ($result_obj->getEntries() as $entry) {
208  $obj_id = $entry['obj_id'];
209  if (isset($new_entries[$obj_id])) {
210  $this->addEntry(
211  $new_entries[$obj_id]['obj_id'],
212  $new_entries[$obj_id]['type'],
213  $new_entries[$obj_id]['found']
214  );
215 
216  $this->__updateEntryChilds(
217  $new_entries[$obj_id]['obj_id'],
218  $new_entries[$obj_id]['child']
219  );
220  }
221  }
222  }
223 
224  public function addResult(int $a_ref_id, int $a_obj_id, string $a_type): void
225  {
226  $this->results[$a_ref_id]['ref_id'] = $a_ref_id;
227  $this->results[$a_ref_id]['obj_id'] = $a_obj_id;
228  $this->results[$a_ref_id]['type'] = $a_type;
229  }
230 
231  public function getResults(): array
232  {
233  return $this->results;
234  }
235 
239  public function getResultIds(): array
240  {
241  $ids = [];
242  foreach ($this->getResults() as $id => $tmp) {
243  $ids[] = $id;
244  }
245  return $ids;
246  }
247 
248  public function getResultsByObjId(): array
249  {
250  $tmp_res = [];
251  foreach ($this->getResults() as $ref_id => $res_data) {
252  $tmp_res[$res_data['obj_id']][] = $ref_id;
253  }
254  return $tmp_res;
255  }
256 
261  public function getUniqueResults(): array
262  {
263  $obj_ids = [];
264  $objects = [];
265  foreach ($this->results as $result) {
266  if (in_array($result['obj_id'], $obj_ids)) {
267  continue;
268  }
269  $obj_ids[] = $result['obj_id'];
270  $objects[] = $result;
271  }
272  return $objects;
273  }
274 
275  public function getResultsForPresentation(): array
276  {
277  $res = [];
278  foreach ($this->getResults() as $result) {
279  if (!is_array($result)) {
280  continue;
281  }
282 
283  $res[(int) $result['ref_id']] = (int) $result['obj_id'];
284  }
285  return $res;
286  }
287 
288  public function getSubitemIds(): array
289  {
290  $res = array();
291  foreach ($this->getResults() as $row) {
292  $res[$row['obj_id']] = $row['child'] ?? [];
293  }
294  return $res;
295  }
296 
302  public function filter(
303  int $a_root_node,
304  bool $check_and,
305  ?ilDate $creation_filter_date_start = null,
306  ?ilDate $creation_filter_date_end = null
307  ): bool {
308  // get ref_ids and check access
309  $counter = 0;
310  $offset_counter = 0;
311  foreach ($this->getEntries() as $entry) {
312  // boolean and failed continue
313  if ($check_and and in_array(0, $entry['found'])) {
314  continue;
315  }
316  // Types like role, rolt, user do not need rbac checks
317  $type = ilObject::_lookupType($entry['obj_id']);
318  if ($type == 'rolt' or $type == 'usr' or $type == 'role') {
319  if ($this->callListeners($entry['obj_id'], $entry)) {
320  $this->addResult($entry['obj_id'], $entry['obj_id'], $type);
321  if (is_array($entry['child'])) {
322  $counter += count($entry['child']);
323  }
324  // Stop if maximum of hits is reached
325  if (++$counter > $this->getMaxHits()) {
326  $this->limit_reached = true;
327  return true;
328  }
329  }
330  continue;
331  }
332 
333  /*
334  * (Re-)check creation date, needed for searches on other tables than obj_data (35275)
335  * Before- and after-operators also allow matching datetimes, see ilObjectSearch::performSearch.
336  */
337  if (!is_null($creation_filter_date_start) || !is_null($creation_filter_date_end)) {
338  if (
339  !ilObject::_exists($entry['obj_id']) ||
340  ($creation_date_string = ilObject::_lookupCreationDate($entry['obj_id'])) === ''
341  ) {
342  continue;
343  }
344  $creation_date = new ilDate(
345  date('Y-m-d', strtotime($creation_date_string)),
347  );
348 
349  if ($creation_filter_date_start && is_null($creation_filter_date_end)) {
350  if (!ilDate::_after($creation_date, $creation_filter_date_start)) {
351  continue;
352  }
353  } elseif ($creation_filter_date_end && is_null($creation_filter_date_start)) {
354  if (!ilDate::_before($creation_date, $creation_filter_date_end)) {
355  continue;
356  }
357  } elseif (!ilDate::_within($creation_date, $creation_filter_date_start, $creation_filter_date_end)) {
358  continue;
359  }
360  }
361 
362  // Check referenced objects
363  foreach (ilObject::_getAllReferences((int) $entry['obj_id']) as $ref_id) {
364  // Failed check: if ref id check is failed by previous search
365  if ($this->search_cache->isFailed($ref_id)) {
366  continue;
367  }
368  // Offset check
369  if ($this->search_cache->isChecked($ref_id) and !$this->isOffsetReached($offset_counter)) {
370  ++$offset_counter;
371  continue;
372  }
373 
374  if (!$this->callListeners($ref_id, $entry)) {
375  continue;
376  }
377 
378 
379 
380  // RBAC check
381  $type = ilObject::_lookupType($ref_id, true);
382  if ($this->ilAccess->checkAccessOfUser(
383  $this->getUserId(),
384  $this->getRequiredPermission(),
385  '',
386  $ref_id,
387  $type,
388  $entry['obj_id']
389  )) {
390  if ($a_root_node == ROOT_FOLDER_ID or $this->tree->isGrandChild($a_root_node, $ref_id)) {
391  // Call listeners
392  #if($this->callListeners($ref_id,$entry))
393  if (1) {
394  $this->addResult($ref_id, $entry['obj_id'], $type);
395  $this->search_cache->appendToChecked($ref_id, $entry['obj_id']);
396  $this->__updateResultChilds($ref_id, $entry['child']);
397 
398  $counter++;
399  $offset_counter++;
400  // Stop if maximum of hits is reached
401 
402  if ($counter >= $this->getMaxHits()) {
403  $this->limit_reached = true;
404  $this->search_cache->setResults($this->results);
405  return true;
406  }
407  }
408  }
409  continue;
410  }
411  $this->search_cache->appendToFailed($ref_id);
412  }
413  }
414  $this->search_cache->setResults($this->results);
415  return false;
416  }
417 
421  public function filterResults(int $a_root_node): void
422  {
423  $tmp_results = $this->getResults();
424  $this->results = array();
425  foreach ($tmp_results as $result) {
426  if (isset($result['ref_id']) && $this->tree->isGrandChild($a_root_node, $result['ref_id']) && $this->tree->isInTree($result['ref_id'])) {
427  $this->addResult($result['ref_id'], $result['obj_id'], $result['type']);
428  $this->__updateResultChilds($result['ref_id'], $result['child'] ?? []);
429  }
430  }
431  }
432 
433  public function save(int $a_type = ilUserSearchCache::DEFAULT_SEARCH): void
434  {
435  $this->search_cache->save();
436  }
437 
438  public function read(int $a_type = ilUserSearchCache::DEFAULT_SEARCH): void
439  {
440  $this->results = $this->search_cache->getResults();
441  }
442 
448  public function __updateEntryChilds(int $a_obj_id, array $a_childs): bool
449  {
450  if ($this->entries[$a_obj_id] and is_array($a_childs)) {
451  foreach ($a_childs as $child_id) {
452  if ($child_id) {
453  $this->entries[$a_obj_id]['child'][$child_id] = $child_id;
454  }
455  }
456  return true;
457  }
458  return false;
459  }
460 
464  public function __updateResultChilds(int $a_ref_id, array $a_childs): bool
465  {
466  if ($this->results[$a_ref_id] and is_array($a_childs)) {
467  foreach ($a_childs as $child_id) {
468  $this->results[$a_ref_id]['child'][$child_id] = $child_id;
469  }
470  return true;
471  }
472  return false;
473  }
474 
475  public function __initSearchSettingsObject(): void
476  {
477  $this->search_settings = new ilSearchSettings();
478  if (!$this->preventOverwritingMaxhits()) {
479  $this->setMaxHits($this->search_settings->getMaxHits());
480  }
481  }
482 
483  protected function initUserSearchCache(): void
484  {
485  $this->search_cache = ilUserSearchCache::_getInstance($this->getUserId());
486  $this->offset = $this->getMaxHits() * ($this->search_cache->getResultPageNumber() - 1) ;
487  }
488 
496  public function preventOverwritingMaxhits(?bool $a_flag = null)
497  {
498  if (null === $a_flag) {
500  }
501 
502  $this->preventOverwritingMaxhits = $a_flag;
503 
504  return $this;
505  }
506 
515  public function addObserver(object $a_class, string $a_method): bool
516  {
517  $this->observers[] = array('class' => $a_class,
518  'method' => $a_method);
519  return true;
520  }
521 
522 
523  public function callListeners(int $a_ref_id, array $a_data): bool
524  {
525  foreach ($this->observers as $observer) {
526  $class = &$observer['class'];
527  $method = $observer['method'];
528 
529  if (!$class->$method($a_ref_id, $a_data)) {
530  return false;
531  }
532  }
533  return true;
534  }
535 }
setMaxHits(int $a_max_hits)
$res
Definition: ltiservices.php:66
read(int $a_type=ilUserSearchCache::DEFAULT_SEARCH)
callListeners(int $a_ref_id, array $a_data)
diffEntriesFromResult()
diff entries of this instance and another result object Used for search in results ...
const ROOT_FOLDER_ID
Definition: constants.php:32
static _before(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
compare two dates and check start is before end This method does not consider tz offsets.
addObserver(object $a_class, string $a_method)
The observer is used to call functions for filtering result.
static _getInstance(int $a_usr_id)
static _getAllReferences(int $id)
get all reference ids for object ID
ilSearchSettings $search_settings
intersectEntries(ilSearchResult $result_obj)
Build intersection of entries (all entries that are present in both result sets)
getUniqueResults()
Get unique results.
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
addResult(int $a_ref_id, int $a_obj_id, string $a_type)
static _exists(int $id, bool $reference=false, ?string $type=null)
checks if an object exists in object_data
setRequiredPermission(string $a_permission)
Set the required permission for the rbac checks in function &#39;filter()&#39;.
$ref_id
Definition: ltiauth.php:65
static _after(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
compare two dates and check start is after end This method does not consider tz offsets.
addEntry(int $a_obj_id, string $a_type, array $found, int $a_child_id=0)
add search result entry Entries are stored with &#39;obj_id&#39;.
__updateResultChilds(int $a_ref_id, array $a_childs)
Update child ids for a specific result.
save(int $a_type=ilUserSearchCache::DEFAULT_SEARCH)
global $DIC
Definition: shib_login.php:26
__construct(int $a_user_id=0)
isOffsetReached(int $a_counter)
Check if offset is reached.
ilUserSearchCache $search_cache
const IL_CAL_DATE
static _lookupCreationDate(int $obj_id)
mergeEntries(ilSearchResult $result_obj)
merge entries of this instance and another result object
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
setUserId(int $a_user_id)
Class for storing search result.
filter(int $a_root_node, bool $check_and, ?ilDate $creation_filter_date_start=null, ?ilDate $creation_filter_date_end=null)
Filter search result.
__updateEntryChilds(int $a_obj_id, array $a_childs)
filterResults(int $a_root_node)
Filter search area of result set.
static _lookupType(int $id, bool $reference=false)
preventOverwritingMaxhits(?bool $a_flag=null)
If you call this function and pass "true" the maxhits setting will not be overwritten in __initSearch...
static _within(ilDateTime $dt, ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
Check whether an date is within a date duration given by start and end.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...