Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
224
src/Application/Search/SearchRequest.php
Normal file
224
src/Application/Search/SearchRequest.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Application\Search;
|
||||
|
||||
use App\Framework\Exception\ErrorCode;
|
||||
use App\Framework\Exception\FrameworkException;
|
||||
use App\Framework\Http\Request;
|
||||
|
||||
/**
|
||||
* Represents a search request with validation
|
||||
*/
|
||||
final readonly class SearchRequest
|
||||
{
|
||||
/**
|
||||
* @param array<string, array> $filters
|
||||
* @param array<string, float> $boosts
|
||||
* @param array<string> $fields
|
||||
* @param array<string> $highlight
|
||||
*/
|
||||
public function __construct(
|
||||
public string $query,
|
||||
public array $filters = [],
|
||||
public array $boosts = [],
|
||||
public array $fields = [],
|
||||
public array $highlight = [],
|
||||
public int $limit = 20,
|
||||
public int $offset = 0,
|
||||
public ?string $sortBy = null,
|
||||
public string $sortDirection = 'asc',
|
||||
public bool $sortByRelevance = true,
|
||||
public bool $enableHighlighting = true,
|
||||
public bool $enableFuzzy = false,
|
||||
public float $minScore = 0.0
|
||||
) {
|
||||
}
|
||||
|
||||
public static function fromHttpRequest(Request $request): self
|
||||
{
|
||||
$query = $request->query;
|
||||
|
||||
// Parse search query
|
||||
$searchQuery = $query->get('q', '*');
|
||||
|
||||
// Parse filters
|
||||
$filters = [];
|
||||
if ($query->has('filters')) {
|
||||
$filtersParam = $query->get('filters');
|
||||
if (is_string($filtersParam)) {
|
||||
$filters = json_decode($filtersParam, true) ?? [];
|
||||
} elseif (is_array($filtersParam)) {
|
||||
$filters = $filtersParam;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse individual filter parameters (filter[field][type]=value)
|
||||
foreach ($query->toArray() as $key => $value) {
|
||||
if (preg_match('/^filter\[([^]]+)](?:\[([^]]+)])?$/', $key, $matches)) {
|
||||
$field = $matches[1];
|
||||
$type = $matches[2] ?? 'equals';
|
||||
|
||||
if (! isset($filters[$field])) {
|
||||
$filters[$field] = [];
|
||||
}
|
||||
|
||||
$filters[$field]['type'] = $type;
|
||||
$filters[$field]['value'] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse boosts
|
||||
$boosts = [];
|
||||
if ($query->has('boosts')) {
|
||||
$boostsParam = $query->get('boosts');
|
||||
if (is_string($boostsParam)) {
|
||||
$boosts = json_decode($boostsParam, true) ?? [];
|
||||
} elseif (is_array($boostsParam)) {
|
||||
$boosts = $boostsParam;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse fields restriction
|
||||
$fields = [];
|
||||
if ($query->has('fields')) {
|
||||
$fieldsParam = $query->get('fields');
|
||||
if (is_string($fieldsParam)) {
|
||||
$fields = array_map('trim', explode(',', $fieldsParam));
|
||||
} elseif (is_array($fieldsParam)) {
|
||||
$fields = $fieldsParam;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse highlight fields
|
||||
$highlight = [];
|
||||
if ($query->has('highlight')) {
|
||||
$highlightParam = $query->get('highlight');
|
||||
if (is_string($highlightParam)) {
|
||||
$highlight = array_map('trim', explode(',', $highlightParam));
|
||||
} elseif (is_array($highlightParam)) {
|
||||
$highlight = $highlightParam;
|
||||
}
|
||||
} elseif ($query->getBool('enable_highlighting', true)) {
|
||||
// Default highlighting for text fields
|
||||
$highlight = ['title', 'content', 'description'];
|
||||
}
|
||||
|
||||
// Parse pagination
|
||||
$limit = min(100, max(1, $query->getInt('limit', 20)));
|
||||
$offset = max(0, $query->getInt('offset', 0));
|
||||
|
||||
// Alternative pagination via page parameter
|
||||
if ($query->has('page')) {
|
||||
$page = max(1, $query->getInt('page', 1));
|
||||
$perPage = min(100, max(1, $query->getInt('per_page', 20)));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
$limit = $perPage;
|
||||
}
|
||||
|
||||
// Parse sorting
|
||||
$sortBy = $query->get('sort_by');
|
||||
$sortDirection = strtolower($query->get('sort_direction', 'asc'));
|
||||
$sortByRelevance = $query->getBool('sort_by_relevance', ! $sortBy);
|
||||
|
||||
// Validate sort direction
|
||||
if (! in_array($sortDirection, ['asc', 'desc'])) {
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
'Invalid sort direction. Must be "asc" or "desc"'
|
||||
);
|
||||
}
|
||||
|
||||
// Parse advanced options
|
||||
$enableHighlighting = $query->getBool('enable_highlighting', true);
|
||||
$enableFuzzy = $query->getBool('enable_fuzzy', false);
|
||||
$minScore = max(0.0, $query->getFloat('min_score', 0.0));
|
||||
|
||||
return new self(
|
||||
query: $searchQuery,
|
||||
filters: $filters,
|
||||
boosts: $boosts,
|
||||
fields: $fields,
|
||||
highlight: $highlight,
|
||||
limit: $limit,
|
||||
offset: $offset,
|
||||
sortBy: $sortBy,
|
||||
sortDirection: $sortDirection,
|
||||
sortByRelevance: $sortByRelevance,
|
||||
enableHighlighting: $enableHighlighting,
|
||||
enableFuzzy: $enableFuzzy,
|
||||
minScore: $minScore
|
||||
);
|
||||
}
|
||||
|
||||
public function validate(): void
|
||||
{
|
||||
if (empty(trim($this->query))) {
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
'Search query cannot be empty'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->limit < 1 || $this->limit > 100) {
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
'Limit must be between 1 and 100'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->offset < 0) {
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
'Offset cannot be negative'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->minScore < 0) {
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
'Minimum score cannot be negative'
|
||||
);
|
||||
}
|
||||
|
||||
// Validate filters
|
||||
foreach ($this->filters as $field => $filterData) {
|
||||
if (! is_array($filterData) || ! isset($filterData['type'])) {
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
"Invalid filter format for field '{$field}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate boosts
|
||||
foreach ($this->boosts as $field => $boost) {
|
||||
if (! is_numeric($boost) || $boost < 0) {
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
"Invalid boost value for field '{$field}'. Must be a positive number"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'query' => $this->query,
|
||||
'filters' => $this->filters,
|
||||
'boosts' => $this->boosts,
|
||||
'fields' => $this->fields,
|
||||
'highlight' => $this->highlight,
|
||||
'limit' => $this->limit,
|
||||
'offset' => $this->offset,
|
||||
'sort_by' => $this->sortBy,
|
||||
'sort_direction' => $this->sortDirection,
|
||||
'sort_by_relevance' => $this->sortByRelevance,
|
||||
'enable_highlighting' => $this->enableHighlighting,
|
||||
'enable_fuzzy' => $this->enableFuzzy,
|
||||
'min_score' => $this->minScore,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user