ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
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 = null,
328  int $creation_filter_operator = 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) && !is_null($creation_filter_operator)) {
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  switch ($creation_filter_operator) {
373  if (ilDate::_before($creation_date, $creation_filter_date)) {
374  continue 2;
375  }
376  break;
377 
379  if (ilDate::_after($creation_date, $creation_filter_date)) {
380  continue 2;
381  }
382  break;
383 
385  if (!ilDate::_equals($creation_date, $creation_filter_date)) {
386  continue 2;
387  }
388  break;
389  }
390  }
391 
392  // Check referenced objects
393  foreach (ilObject::_getAllReferences((int) $entry['obj_id']) as $ref_id) {
394  // Failed check: if ref id check is failed by previous search
395  if ($this->search_cache->isFailed($ref_id)) {
396  continue;
397  }
398  // Offset check
399  if ($this->search_cache->isChecked($ref_id) and !$this->isOffsetReached($offset_counter)) {
400  ++$offset_counter;
401  continue;
402  }
403 
404  if (!$this->callListeners($ref_id, $entry)) {
405  continue;
406  }
407 
408 
409 
410  // RBAC check
411  $type = ilObject::_lookupType($ref_id, true);
412  if ($this->ilAccess->checkAccessOfUser(
413  $this->getUserId(),
414  $this->getRequiredPermission(),
415  '',
416  $ref_id,
417  $type,
418  $entry['obj_id']
419  )) {
420  if ($a_root_node == ROOT_FOLDER_ID or $this->tree->isGrandChild($a_root_node, $ref_id)) {
421  // Call listeners
422  #if($this->callListeners($ref_id,$entry))
423  if (1) {
424  $this->addResult($ref_id, $entry['obj_id'], $type);
425  $this->search_cache->appendToChecked($ref_id, $entry['obj_id']);
426  $this->__updateResultChilds($ref_id, $entry['child']);
427 
428  $counter++;
429  $offset_counter++;
430  // Stop if maximum of hits is reached
431 
432  if ($counter >= $this->getMaxHits()) {
433  $this->limit_reached = true;
434  $this->search_cache->setResults($this->results);
435  return true;
436  }
437  }
438  }
439  continue;
440  }
441  $this->search_cache->appendToFailed($ref_id);
442  }
443  }
444  $this->search_cache->setResults($this->results);
445  return false;
446  }
447 
453  public function filterResults(int $a_root_node): void
454  {
455  $tmp_results = $this->getResults();
456  $this->results = array();
457  foreach ($tmp_results as $result) {
458  if ($this->tree->isGrandChild($a_root_node, $result['ref_id']) && $this->tree->isInTree($result['ref_id'])) {
459  $this->addResult($result['ref_id'], $result['obj_id'], $result['type']);
460  $this->__updateResultChilds($result['ref_id'], $result['child'] ?? []);
461  }
462  }
463  }
464 
465 
471  public function save(int $a_type = ilUserSearchCache::DEFAULT_SEARCH): void
472  {
473  $this->search_cache->save();
474  }
481  public function read(int $a_type = ilUserSearchCache::DEFAULT_SEARCH): void
482  {
483  $this->results = $this->search_cache->getResults();
484  }
485 
486  // PRIVATE
494  public function __updateEntryChilds(int $a_obj_id, array $a_childs): bool
495  {
496  if ($this->entries[$a_obj_id] and is_array($a_childs)) {
497  foreach ($a_childs as $child_id) {
498  if ($child_id) {
499  $this->entries[$a_obj_id]['child'][$child_id] = $child_id;
500  }
501  }
502  return true;
503  }
504  return false;
505  }
509  public function __updateResultChilds(int $a_ref_id, array $a_childs): bool
510  {
511  if ($this->results[$a_ref_id] and is_array($a_childs)) {
512  foreach ($a_childs as $child_id) {
513  $this->results[$a_ref_id]['child'][$child_id] = $child_id;
514  }
515  return true;
516  }
517  return false;
518  }
519 
520 
521 
522  public function __initSearchSettingsObject(): void
523  {
524  $this->search_settings = new ilSearchSettings();
525  if (!$this->preventOverwritingMaxhits()) {
526  $this->setMaxHits($this->search_settings->getMaxHits());
527  }
528  }
529 
536  protected function initUserSearchCache(): void
537  {
538  $this->search_cache = ilUserSearchCache::_getInstance($this->getUserId());
539  $this->offset = $this->getMaxHits() * ($this->search_cache->getResultPageNumber() - 1) ;
540  }
541 
549  public function preventOverwritingMaxhits(?bool $a_flag = null)
550  {
551  if (null === $a_flag) {
553  }
554 
555  $this->preventOverwritingMaxhits = $a_flag;
556 
557  return $this;
558  }
559 
568  public function addObserver(object $a_class, string $a_method): bool
569  {
570  $this->observers[] = array('class' => $a_class,
571  'method' => $a_method);
572  return true;
573  }
574 
575 
576  public function callListeners(int $a_ref_id, array $a_data): bool
577  {
578  foreach ($this->observers as $observer) {
579  $class = &$observer['class'];
580  $method = $observer['method'];
581 
582  if (!$class->$method($a_ref_id, $a_data)) {
583  return false;
584  }
585  }
586  return true;
587  }
588 } // 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
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)
global $DIC
Definition: feed.php:28
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:67
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.
filter(int $a_root_node, bool $check_and, ilDate $creation_filter_date=null, int $creation_filter_operator=null)
Filter search result.
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.
__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
setUserId(int $a_user_id)
Class for storing search result.
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
numEntries()
Check number of entries public.
static _equals(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
Check if two date are equal.
__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...