Wire admin studio SSR and search infrastructure
This commit is contained in:
@@ -9,21 +9,32 @@ use App\Http\Resources\ArtworkListResource;
|
||||
use App\Services\ArtworkSearchService;
|
||||
use App\Services\GroupDiscoveryService;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\View\View;
|
||||
use cPad\Plugins\News\Models\NewsArticle;
|
||||
|
||||
final class SearchController extends Controller
|
||||
{
|
||||
private const ALLOWED_SORTS = ['latest', 'popular', 'likes', 'downloads'];
|
||||
|
||||
public function __construct(
|
||||
private readonly ArtworkSearchService $search,
|
||||
private readonly GroupDiscoveryService $groups,
|
||||
) {}
|
||||
|
||||
public function index(Request $request): View
|
||||
public function index(Request $request): View|RedirectResponse
|
||||
{
|
||||
$q = trim((string) $request->query('q', ''));
|
||||
$sort = $request->query('sort', 'latest');
|
||||
$canonicalQuery = $this->canonicalQueryParameters($request);
|
||||
$canonicalUrl = $this->canonicalSearchUrl($request, $canonicalQuery);
|
||||
|
||||
if ($request->fullUrl() !== $canonicalUrl) {
|
||||
return redirect()->to($canonicalUrl, 301);
|
||||
}
|
||||
|
||||
$q = (string) ($canonicalQuery['q'] ?? '');
|
||||
$sort = (string) ($canonicalQuery['sort'] ?? 'latest');
|
||||
$hasQuery = $q !== '';
|
||||
|
||||
$sortMap = [
|
||||
@@ -98,4 +109,81 @@ final class SearchController extends Controller
|
||||
'page_robots' => 'noindex,follow',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, int|string>
|
||||
*/
|
||||
private function canonicalQueryParameters(Request $request): array
|
||||
{
|
||||
$q = $this->normalizeSearchQuery($request->query('q', ''));
|
||||
|
||||
if ($q === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$params = ['q' => $q];
|
||||
$sort = $this->normalizeSort($request->query('sort', 'latest'));
|
||||
$page = $this->normalizePage($request->query('page', 1));
|
||||
|
||||
if ($sort !== 'latest') {
|
||||
$params['sort'] = $sort;
|
||||
}
|
||||
|
||||
if ($page > 1) {
|
||||
$params['page'] = $page;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int|string> $params
|
||||
*/
|
||||
private function canonicalSearchUrl(Request $request, array $params): string
|
||||
{
|
||||
$query = Arr::query($params);
|
||||
|
||||
return $query === '' ? $request->url() : $request->url() . '?' . $query;
|
||||
}
|
||||
|
||||
private function normalizeSearchQuery(mixed $value): string
|
||||
{
|
||||
$query = html_entity_decode($this->firstScalarValue($value), ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$query = preg_replace('/(?:\?|&)(?:amp;)?(?:page|sort|filter|group|id|txtfilter|q)=.*$/i', '', $query) ?? $query;
|
||||
$query = preg_replace('/\s+/u', ' ', $query) ?? $query;
|
||||
|
||||
return trim($query, " \t\n\r\0\x0B?&");
|
||||
}
|
||||
|
||||
private function normalizeSort(mixed $value): string
|
||||
{
|
||||
$sort = strtolower($this->firstScalarValue($value));
|
||||
$sort = preg_replace('/(?:\?|&).*/', '', $sort) ?? $sort;
|
||||
|
||||
return in_array($sort, self::ALLOWED_SORTS, true) ? $sort : 'latest';
|
||||
}
|
||||
|
||||
private function normalizePage(mixed $value): int
|
||||
{
|
||||
$page = $this->firstScalarValue($value);
|
||||
|
||||
if (preg_match('/\d+/', $page, $matches) !== 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return max(1, (int) $matches[0]);
|
||||
}
|
||||
|
||||
private function firstScalarValue(mixed $value): string
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$value = reset($value);
|
||||
}
|
||||
|
||||
if (! is_scalar($value) && $value !== null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return trim((string) $value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user