2026-02-11 19:28:12 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace MintyPHP\Repository\Support;
|
|
|
|
|
|
|
|
|
|
class RepoQuery
|
|
|
|
|
{
|
|
|
|
|
private static function normalizeLikeValue(string $value): string
|
|
|
|
|
{
|
|
|
|
|
$value = trim($value);
|
|
|
|
|
if ($value === '') {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
$value = str_replace('\\', '\\\\', $value);
|
|
|
|
|
$value = str_replace('_', '\\_', $value);
|
|
|
|
|
$value = str_replace('*', '%', $value);
|
|
|
|
|
if (!str_starts_with($value, '%')) {
|
|
|
|
|
$value = '%' . $value;
|
|
|
|
|
}
|
|
|
|
|
if (!str_ends_with($value, '%')) {
|
|
|
|
|
$value = $value . '%';
|
|
|
|
|
}
|
|
|
|
|
return $value;
|
|
|
|
|
}
|
|
|
|
|
public static function sanitizeLimitOffset(
|
|
|
|
|
array $options,
|
|
|
|
|
int $defaultLimit = 10,
|
|
|
|
|
int $minLimit = 1,
|
|
|
|
|
int $maxLimit = 100,
|
|
|
|
|
int $defaultOffset = 0
|
|
|
|
|
): array {
|
|
|
|
|
$limit = (int) ($options['limit'] ?? $defaultLimit);
|
|
|
|
|
if ($limit < $minLimit) {
|
|
|
|
|
$limit = $defaultLimit;
|
|
|
|
|
} elseif ($limit > $maxLimit) {
|
|
|
|
|
$limit = $maxLimit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$offset = (int) ($options['offset'] ?? $defaultOffset);
|
|
|
|
|
if ($offset < 0) {
|
|
|
|
|
$offset = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [$limit, $offset];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function sanitizeOrder(
|
|
|
|
|
array $options,
|
|
|
|
|
array $allowedOrder,
|
|
|
|
|
string $defaultOrder = 'id',
|
|
|
|
|
string $defaultDir = 'desc'
|
|
|
|
|
): array {
|
|
|
|
|
$order = (string) ($options['order'] ?? $defaultOrder);
|
|
|
|
|
$dir = strtolower((string) ($options['dir'] ?? $defaultDir));
|
|
|
|
|
if (!in_array($order, $allowedOrder, true)) {
|
|
|
|
|
$order = $defaultOrder;
|
|
|
|
|
}
|
|
|
|
|
if (!in_array($dir, ['asc', 'desc'], true)) {
|
|
|
|
|
$dir = $defaultDir;
|
|
|
|
|
}
|
|
|
|
|
return [$order, $dir];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function addLikeFilter(array &$where, array &$params, array $fields, string $value): void
|
|
|
|
|
{
|
|
|
|
|
$like = self::normalizeLikeValue($value);
|
|
|
|
|
if ($like === '') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$clauses = [];
|
|
|
|
|
foreach ($fields as $field) {
|
|
|
|
|
$clauses[] = $field . " like ? escape '\\\\'";
|
|
|
|
|
$params[] = $like;
|
|
|
|
|
}
|
|
|
|
|
if ($clauses) {
|
|
|
|
|
$where[] = '(' . implode(' or ', $clauses) . ')';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function addEnumFilter(array &$where, array &$params, $value, array $map): void
|
|
|
|
|
{
|
|
|
|
|
if ($value === null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$normalized = strtolower(trim((string) $value));
|
|
|
|
|
if ($normalized === '' || $normalized === 'all') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
foreach ($map as $entry) {
|
|
|
|
|
$aliases = $entry['aliases'] ?? [];
|
|
|
|
|
$aliases = array_map('strtolower', $aliases);
|
|
|
|
|
if (!in_array($normalized, $aliases, true)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$sql = (string) ($entry['sql'] ?? '');
|
|
|
|
|
if ($sql === '') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$where[] = $sql;
|
|
|
|
|
$paramsToAdd = $entry['params'] ?? [];
|
|
|
|
|
foreach ($paramsToAdd as $param) {
|
|
|
|
|
$params[] = $param;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
public static function uuidV4(): string
|
|
|
|
|
{
|
|
|
|
|
$data = random_bytes(16);
|
|
|
|
|
$data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
|
|
|
|
|
$data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
|
|
|
|
|
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 19:28:12 +01:00
|
|
|
public static function addEqualsFilter(array &$where, array &$params, $value, string $sql, bool $allowEmpty = false): void
|
|
|
|
|
{
|
|
|
|
|
// Use for simple "= ?" filters; skip when empty to keep WHERE clean.
|
|
|
|
|
if ($value === null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$normalized = trim((string) $value);
|
|
|
|
|
if ($normalized === '' && !$allowEmpty) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$where[] = $sql;
|
|
|
|
|
$params[] = $normalized;
|
|
|
|
|
}
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
|
|
|
|
|
public static function normalizeIdList($value): array
|
|
|
|
|
{
|
|
|
|
|
if (is_string($value)) {
|
|
|
|
|
$value = array_filter(array_map('trim', explode(',', $value)));
|
|
|
|
|
} elseif (!is_array($value)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
$flat = [];
|
|
|
|
|
array_walk_recursive($value, static function ($item) use (&$flat): void {
|
|
|
|
|
$flat[] = $item;
|
|
|
|
|
});
|
|
|
|
|
$ids = [];
|
|
|
|
|
foreach ($flat as $item) {
|
|
|
|
|
$id = (int) trim((string) $item);
|
|
|
|
|
if ($id > 0) {
|
|
|
|
|
$ids[] = $id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$ids = array_values(array_unique($ids));
|
|
|
|
|
sort($ids, SORT_NUMERIC);
|
|
|
|
|
return $ids;
|
|
|
|
|
}
|
2026-03-04 15:56:58 +01:00
|
|
|
|
|
|
|
|
public static function normalizeStringList(mixed $value, int $max = 200, ?callable $sanitizer = null): array
|
|
|
|
|
{
|
|
|
|
|
if ($max <= 0) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_string($value)) {
|
|
|
|
|
$value = array_filter(array_map('trim', explode(',', $value)));
|
|
|
|
|
} elseif (!is_array($value)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$flat = [];
|
|
|
|
|
array_walk_recursive($value, static function ($item) use (&$flat): void {
|
|
|
|
|
$flat[] = $item;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$items = [];
|
|
|
|
|
$seen = [];
|
|
|
|
|
foreach ($flat as $item) {
|
|
|
|
|
$text = trim((string) $item);
|
|
|
|
|
if ($text === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($sanitizer !== null) {
|
|
|
|
|
$text = trim((string) $sanitizer($text));
|
|
|
|
|
if ($text === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isset($seen[$text])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$seen[$text] = true;
|
|
|
|
|
$items[] = $text;
|
|
|
|
|
if (count($items) >= $max) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $items;
|
|
|
|
|
}
|
2026-02-11 19:28:12 +01:00
|
|
|
}
|