0) { PermissionService::getUserPermissions($userId); } $query = trim((string) ($_GET['search'] ?? '')); if ($query === '') { Router::json(['data' => [], 'total' => 0]); } $limit = (int) ($_GET['limit'] ?? 10); if ($limit < 1) { $limit = 10; } elseif ($limit > 100) { $limit = 100; } $offset = (int) ($_GET['offset'] ?? 0); if ($offset < 0) { $offset = 0; } $locale = I18n::$locale ?? (I18n::$defaultLocale ?? 'de'); $tenantIds = $userId > 0 ? UserTenantRepository::listTenantIdsByUserId($userId) : []; $tenantScoped = !empty($tenantIds); $tenantScopeFilters = SearchConfig::tenantScopeFilters(); $resources = SearchConfig::resources($query, $locale); $items = []; foreach ($resources as $resource) { $key = (string) ($resource['key'] ?? ''); if ($key === '') { continue; } $perm = (string) ($resource['permission'] ?? ''); if ($perm !== '' && !PermissionService::userHas($userId, $perm)) { continue; } $tenantFilter = $tenantScopeFilters[$key] ?? ''; $isTenantScoped = $tenantFilter !== ''; if ($isTenantScoped && !$tenantScoped) { continue; } $sql = $resource['resultSql'] ?? ''; if ($sql === '') { continue; } $sql = str_replace('{{tenantFilter}}', $isTenantScoped ? $tenantFilter : '', $sql); $params = $resource['resultParams'] ?? []; if ($isTenantScoped && strpos($sql, '???') !== false) { $params[] = $tenantIds; } $rows = DB::select($sql, ...$params); foreach ($rows as $row) { $data = $row; if (is_array($row) && count($row) === 1) { $data = reset($row); } $data = is_array($data) ? $data : []; $mapped = SearchConfig::mapResultItem($key, (string) ($resource['label'] ?? ''), $data); if ($mapped !== null) { $items[] = $mapped; } } } $queryLower = mb_strtolower($query); $scoreItem = static function (array $item) use ($queryLower): int { $title = mb_strtolower((string) ($item['title'] ?? '')); $description = mb_strtolower((string) ($item['description'] ?? '')); if ($title === $queryLower) { return 400; } if ($title !== '' && strpos($title, $queryLower) === 0) { return 300; } if ($title !== '' && strpos($title, $queryLower) !== false) { return 200; } if ($description !== '' && strpos($description, $queryLower) !== false) { return 100; } return 0; }; usort($items, static function (array $a, array $b) use ($scoreItem): int { $scoreA = $scoreItem($a); $scoreB = $scoreItem($b); if ($scoreA !== $scoreB) { return $scoreB <=> $scoreA; } $title = strcmp((string) ($a['title'] ?? ''), (string) ($b['title'] ?? '')); if ($title !== 0) { return $title; } return strcmp((string) ($a['type'] ?? ''), (string) ($b['type'] ?? '')); }); $total = count($items); $items = array_slice($items, $offset, $limit); Router::json([ 'data' => $items, 'total' => $total, ]);