ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
class.ilSearchResult.php
Go to the documentation of this file.
1 <?php
2 
31 {
32  private string $permission = 'visible';
33 
34  private int $user_id;
35  private array $entries = array();
36  private array $results = array();
37  private array $observers = array();
38  private int $max_hits = 0;
39 
41  protected int $offset = 0;
42 
43  // OBJECT VARIABLES
44  protected ilAccess $ilAccess;
45  protected ilDBInterface $db;
46  protected ilTree $tree;
47  protected ilObjUser $user;
49 
50  // Stores info if MAX HITS is reached or not
51  public bool $limit_reached = false;
52 
53  protected bool $preventOverwritingMaxhits = false;
54 
55  protected ilLogger $logger;
56 
57 
58 
63  public function __construct(int $a_user_id = 0)
64  {
65  global $DIC;
66 
67  $this->logger = $DIC->logger()->src();
68  $this->ilAccess = $DIC->access();
69  $this->db = $DIC->database();
70  $this->tree = $DIC->repositoryTree();
71  $this->user = $DIC->user();
72 
73  if ($a_user_id) {
74  $this->user_id = $a_user_id;
75  } else {
76  $this->user_id = $this->user->getId();
77  }
79  $this->initUserSearchCache();
80  }
81 
85  public function setRequiredPermission(string $a_permission): void
86  {
87  $this->permission = $a_permission;
88  }
89 
90  public function getRequiredPermission(): string
91  {
92  return $this->permission;
93  }
94 
95 
96  public function setUserId(int $a_user_id): void
97  {
98  $this->user_id = $a_user_id;
99  }
100  public function getUserId(): int
101  {
102  return $this->user_id;
103  }
104 
105  public function getEntries(): array
106  {
107  return $this->entries;
108  }
109 
110  public function isLimitReached(): bool
111  {
112  return $this->limit_reached;
113  }
114 
115  public function setMaxHits(int $a_max_hits): void
116  {
117  $this->max_hits = $a_max_hits;
118  }
119  public function getMaxHits(): int
120  {
121  return $this->max_hits;
122  }
123 
127  public function isOffsetReached(int $a_counter): bool
128  {
129  return !($a_counter < $this->offset);
130  }
131 
142  public function addEntry(int $a_obj_id, string $a_type, array $found, int $a_child_id = 0): void
143  {
144  // Create new entry if it not exists
145  if (!isset($this->entries[$a_obj_id])) {
146  $this->entries[$a_obj_id]['obj_id'] = $a_obj_id;
147  $this->entries[$a_obj_id]['type'] = $a_type;
148  $this->entries[$a_obj_id]['found'] = $found;
149  $this->entries[$a_obj_id]['child'] = [];
150 
151  if ($a_child_id and $a_child_id != $a_obj_id) {
152  $this->entries[$a_obj_id]['child'][$a_child_id] = $a_child_id;
153  }
154  } else {
155  // replace or add child ('pg','st') id
156  if ($a_child_id and $a_child_id != $a_obj_id) {
157  $this->entries[$a_obj_id]['child'][$a_child_id] = $a_child_id;
158  }
159  $counter = 0;
160  foreach ($found as $position) {
161  if ($position) {
162  $this->entries[$a_obj_id]['found'][$counter] = $position;
163  }
164  $counter++;
165  }
166  }
167  }
168 
174  public function numEntries(): int
175  {
176  return count($this->getEntries());
177  }
178 
185  public function mergeEntries(ilSearchResult $result_obj): void
186  {
187  foreach ($result_obj->getEntries() as $obj_id => $entry) {
188  $this->addEntry($entry['obj_id'], $entry['type'], $entry['found']);
189  $this->__updateEntryChilds($entry['obj_id'], $entry['child']);
190  }
191  }
192 
197  public function diffEntriesFromResult(): void
198  {
199  $new_entries = $this->getEntries();
200  $this->entries = array();
201 
202  // Get all checked objects
203  foreach ($this->search_cache->getCheckedItems() as $ref_id => $obj_id) {
204  if (isset($new_entries[$obj_id])) {
205  $this->addEntry(
206  $new_entries[$obj_id]['obj_id'],
207  $new_entries[$obj_id]['type'],
208  $new_entries[$obj_id]['found']
209  );
210  $this->__updateEntryChilds(
211  $new_entries[$obj_id]['obj_id'],
212  $new_entries[$obj_id]['child']
213  );
214  }
215  }
216  }
217 
221  public function intersectEntries(ilSearchResult $result_obj): void
222  {
223  $new_entries = $this->getEntries();
224  $this->entries = [];
225  foreach ($result_obj->getEntries() as $entry) {
226  $obj_id = $entry['obj_id'];
227  if (isset($new_entries[$obj_id])) {
228  $this->addEntry(
229  $new_entries[$obj_id]['obj_id'],
230  $new_entries[$obj_id]['type'],
231  $new_entries[$obj_id]['found']
232  );
233 
234  $this->__updateEntryChilds(
235  $new_entries[$obj_id]['obj_id'],
236  $new_entries[$obj_id]['child']
237  );
238  }
239  }
240  }
241 
242  public function addResult(int $a_ref_id, int $a_obj_id, string $a_type): void
243  {
244  $this->results[$a_ref_id]['ref_id'] = $a_ref_id;
245  $this->results[$a_ref_id]['obj_id'] = $a_obj_id;
246  $this->results[$a_ref_id]['type'] = $a_type;
247  }
248 
249  public function getResults(): array
250  {
251  return $this->results;
252  }
253 
258  public function getResultIds(): array
259  {
260  $ids = [];
261  foreach ($this->getResults() as $id => $tmp) {
262  $ids[] = $id;
263  }
264  return $ids;
265  }
266 
267  public function getResultsByObjId(): array
268  {
269  $tmp_res = [];
270  foreach ($this->getResults() as $ref_id => $res_data) {
271  $tmp_res[$res_data['obj_id']][] = $ref_id;
272  }
273  return $tmp_res;
274  }
275 
276 
281  public function getUniqueResults(): array
282  {
283  $obj_ids = [];
284  $objects = [];
285  foreach ($this->results as $result) {
286  if (in_array($result['obj_id'], $obj_ids)) {
287  continue;
288  }
289  $obj_ids[] = $result['obj_id'];
290  $objects[] = $result;
291  }
292  return $objects;
293  }
294 
295  public function getResultsForPresentation(): array
296  {
297  $res = [];
298  foreach ($this->getResults() as $result) {
299  if (!is_array($result)) {
300  continue;
301  }
302 
303  $res[(int) $result['ref_id']] = (int) $result['obj_id'];
304  }
305  return $res;
306  }
307 
308  public function getSubitemIds(): array
309  {
310  $res = array();
311  foreach ($this->getResults() as $row) {
312  $res[$row['obj_id']] = $row['child'] ?? [];
313  }
314  return $res;
315  }
316 
317 
318 
324  public function filter(
325  int $a_root_node,
326  bool $check_and,
327  ilDate $creation_filter_date_start = null,
328  ilDate $creation_filter_date_end = null
329  ): bool {
330  // get ref_ids and check access
331  $counter = 0;
332  $offset_counter = 0;
333  foreach ($this->getEntries() as $entry) {
334  // boolean and failed continue
335  if ($check_and and in_array(0, $entry['found'])) {
336  continue;
337  }
338  // Types like role, rolt, user do not need rbac checks
339  $type = ilObject::_lookupType($entry['obj_id']);
340  if ($type == 'rolt' or $type == 'usr' or $type == 'role') {
341  if ($this->callListeners($entry['obj_id'], $entry)) {
342  $this->addResult($entry['obj_id'], $entry['obj_id'], $type);
343  if (is_array($entry['child'])) {
344  $counter += count($entry['child']);
345  }
346  // Stop if maximum of hits is reached
347  if (++$counter > $this->getMaxHits()) {
348  $this->limit_reached = true;
349  return true;
350  }
351  }
352  continue;
353  }
354 
355  /*
356  * (Re-)check creation date, needed for searches on other tables than obj_data (35275)
357  * Before- and after-operators also allow matching datetimes, see ilObjectSearch::performSearch.
358  */
359  if (!is_null($creation_filter_date_start) || !is_null($creation_filter_date_end)) {
360  if (
361  !ilObject::_exists($entry['obj_id']) ||
362  ($creation_date_string = ilObject::_lookupCreationDate($entry['obj_id'])) === ''
363  ) {
364  continue;
365  }
366  $creation_date = new ilDate(
367  date('Y-m-d', strtotime($creation_date_string)),
369  );
370 
371  if ($creation_filter_date_start && is_null($creation_filter_date_end)) {
372  if (!ilDate::_after($creation_date, $creation_filter_date_start)) {
373  continue;
374  }
375  } elseif ($creation_filter_date_end && is_null($creation_filter_date_start)) {
376  if (!ilDate::_before($creation_date, $creation_filter_date_end)) {
377  continue;
378  }
379  } else {
380  if (!ilDate::_within($creation_date, $creation_filter_date_start, $creation_filter_date_end)) {
381  continue;
382  }
383  }
384  }
385 
386  // Check referenced objects
387  foreach (ilObject::_getAllReferences((int) $entry['obj_id']) as $ref_id) {
388  // Failed check: if ref id check is failed by previous search
389  if ($this->search_cache->isFailed($ref_id)) {
390  continue;
391  }
392  // Offset check
393  if ($this->search_cache->isChecked($ref_id) and !$this->isOffsetReached($offset_counter)) {
394  ++$offset_counter;
395  continue;
396  }
397 
398  if (!$this->callListeners($ref_id, $entry)) {
399  continue;
400  }
401 
402 
403 
404  // RBAC check
405  $type = ilObject::_lookupType($ref_id, true);
406  if ($this->ilAccess->checkAccessOfUser(
407  $this->getUserId(),
408  $this->getRequiredPermission(),
409  '',
410  $ref_id,
411  $type,
412  $entry['obj_id']
413  )) {
414  if ($a_root_node == ROOT_FOLDER_ID or $this->tree->isGrandChild($a_root_node, $ref_id)) {
415  // Call listeners
416  #if($this->callListeners($ref_id,$entry))
417  if (1) {
418  $this->addResult($ref_id, $entry['obj_id'], $type);
419  $this->search_cache->appendToChecked($ref_id, $entry['obj_id']);
420  $this->__updateResultChilds($ref_id, $entry['child']);
421 
422  $counter++;
423  $offset_counter++;
424  // Stop if maximum of hits is reached
425 
426  if ($counter >= $this->getMaxHits()) {
427  $this->limit_reached = true;
428  $this->search_cache->setResults($this->results);
429  return true;
430  }
431  }
432  }
433  continue;
434  }
435  $this->search_cache->appendToFailed($ref_id);
436  }
437  }
438  $this->search_cache->setResults($this->results);
439  return false;
440  }
441 
447  public function filterResults(int $a_root_node): void
448  {
449  $tmp_results = $this->getResults();
450  $this->results = array();
451  foreach ($tmp_results as $result) {
452  if (isset($result['ref_id']) && $this->tree->isGrandChild($a_root_node, $result['ref_id']) && $this->tree->isInTree($result['ref_id'])) {
453  $this->addResult($result['ref_id'], $result['obj_id'], $result['type']);
454  $this->__updateResultChilds($result['ref_id'], $result['child'] ?? []);
455  }
456  }
457  }
458 
459 
465  public function save(int $a_type = ilUserSearchCache::DEFAULT_SEARCH): void
466  {
467  $this->search_cache->save();
468  }
475  public function read(int $a_type = ilUserSearchCache::DEFAULT_SEARCH): void
476  {
477  $this->results = $this->search_cache->getResults();
478  }
479 
480  // PRIVATE
488  public function __updateEntryChilds(int $a_obj_id, array $a_childs): bool
489  {
490  if ($this->entries[$a_obj_id] and is_array($a_childs)) {
491  foreach ($a_childs as $child_id) {
492  if ($child_id) {
493  $this->entries[$a_obj_id]['child'][$child_id] = $child_id;
494  }
495  }
496  return true;
497  }
498  return false;
499  }
503  public function __updateResultChilds(int $a_ref_id, array $a_childs): bool
504  {
505  if ($this->results[$a_ref_id] and is_array($a_childs)) {
506  foreach ($a_childs as $child_id) {
507  $this->results[$a_ref_id]['child'][$child_id] = $child_id;
508  }
509  return true;
510  }
511  return false;
512  }
513 
514 
515 
516  public function __initSearchSettingsObject(): void
517  {
518  $this->search_settings = new ilSearchSettings();
519  if (!$this->preventOverwritingMaxhits()) {
520  $this->setMaxHits($this->search_settings->getMaxHits());
521  }
522  }
523 
530  protected function initUserSearchCache(): void
531  {
532  $this->search_cache = ilUserSearchCache::_getInstance($this->getUserId());
533  $this->offset = $this->getMaxHits() * ($this->search_cache->getResultPageNumber() - 1) ;
534  }
535 
543  public function preventOverwritingMaxhits(?bool $a_flag = null)
544  {
545  if (null === $a_flag) {
547  }
548 
549  $this->preventOverwritingMaxhits = $a_flag;
550 
551  return $this;
552  }
553 
562  public function addObserver(object $a_class, string $a_method): bool
563  {
564  $this->observers[] = array('class' => $a_class,
565  'method' => $a_method);
566  return true;
567  }
568 
569 
570  public function callListeners(int $a_ref_id, array $a_data): bool
571  {
572  foreach ($this->observers as $observer) {
573  $class = &$observer['class'];
574  $method = $observer['method'];
575 
576  if (!$class->$method($a_ref_id, $a_data)) {
577  return false;
578  }
579  }
580  return true;
581  }
582 } // END class.Search
setMaxHits(int $a_max_hits)
$res
Definition: ltiservices.php:69
read(int $a_type=ilUserSearchCache::DEFAULT_SEARCH)
read search results
callListeners(int $a_ref_id, array $a_data)
diffEntriesFromResult()
diff entries of this instance and another result object Used for search in results ...
getResultIds()
get result ids
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
initUserSearchCache()
Init user search cache.
ilSearchSettings $search_settings
filter(int $a_root_node, bool $check_and, ilDate $creation_filter_date_start=null, ilDate $creation_filter_date_end=null)
Filter search result.
intersectEntries(ilSearchResult $result_obj)
Build intersection of entries (all entries that are present in both result sets)
getUniqueResults()
Get unique results.
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:66
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)
Save search results.
global $DIC
Definition: shib_login.php:25
__construct(int $a_user_id=0)
Constructor public.
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:24
setUserId(int $a_user_id)
Class for storing search result.
numEntries()
Check number of entries public.
__updateEntryChilds(int $a_obj_id, array $a_childs)
Update childs for a specific entry.
filterResults(int $a_root_node)
Filter search area of result set public.
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.