Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.91% covered (success)
90.91%
30 / 33
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SearchSettingsApiController
90.62% covered (success)
90.62%
29 / 32
50.00% covered (danger)
50.00%
2 / 4
8.05
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSettings
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
 saveSettings
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
3.00
 jsonResponse
60.00% covered (warning)
60.00%
3 / 5
0.00% covered (danger)
0.00%
0 / 1
2.26
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Core\Search\Presentation\Api;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Engine\Application\Security\PermissionContextFactory;
12use App\Core\Search\Application\Service\GlobalSearchServiceInterface;
13use App\Core\Search\Domain\Model\SearchSettings;
14use JsonException;
15use Psr\Http\Message\ResponseFactoryInterface;
16use Psr\Http\Message\ResponseInterface;
17use Psr\Http\Message\ServerRequestInterface;
18
19/**
20 * Search Settings REST API Controller.
21 *
22 * Manages user-specific search configuration (modules, fields, modes).
23 *
24 * @package App\Core\Search\Presentation\Api
25 */
26final readonly class SearchSettingsApiController
27{
28    /**
29     * SearchSettingsApiController constructor.
30     *
31     * @param GlobalSearchServiceInterface $searchService  Search application service.
32     * @param PermissionContextFactory     $contextFactory Security context factory.
33     * @param ResponseFactoryInterface     $factory        PSR-7 response factory.
34     */
35    public function __construct(
36        private GlobalSearchServiceInterface $searchService,
37        private PermissionContextFactory     $contextFactory,
38        private ResponseFactoryInterface     $factory
39    ) {
40    }
41
42    /**
43     * Returns current user search settings and list of configurable modules with fields.
44     *
45     * @param ServerRequestInterface $request HTTP server request.
46     * @return ResponseInterface JSON API response.
47     */
48    public function getSettings(ServerRequestInterface $request): ResponseInterface
49    {
50        $context = $this->contextFactory->createFromRequest($request);
51        if (!$context->isAuthenticated()) {
52            return $this->jsonResponse(['success' => false, 'error' => 'Authentication required.'], 401);
53        }
54
55        $settings = $this->searchService->getSearchSettings($context->actorUserId);
56        $modules = $this->searchService->getSearchableModules($context);
57
58        return $this->jsonResponse([
59            'success' => true,
60            'data'    => [
61                'settings' => $settings->jsonSerialize(),
62                'modules'  => $modules,
63            ],
64        ]);
65    }
66
67    /**
68     * Saves updated search settings for current user.
69     *
70     * @param ServerRequestInterface $request HTTP server request.
71     * @return ResponseInterface JSON API response.
72     */
73    public function saveSettings(ServerRequestInterface $request): ResponseInterface
74    {
75        $context = $this->contextFactory->createFromRequest($request);
76        if (!$context->isAuthenticated()) {
77            return $this->jsonResponse(['success' => false, 'error' => 'Authentication required.'], 401);
78        }
79
80        $body = (string) $request->getBody();
81        $payload = json_decode($body, true);
82        if (!is_array($payload)) {
83            return $this->jsonResponse(['success' => false, 'error' => 'Invalid JSON payload.'], 400);
84        }
85
86        $settings = SearchSettings::fromArray($payload);
87        $this->searchService->saveSearchSettings($context->actorUserId, $settings);
88
89        return $this->jsonResponse([
90            'success' => true,
91            'message' => 'Search settings have been saved successfully.',
92            'data'    => $settings->jsonSerialize(),
93        ]);
94    }
95
96    /**
97     * Builds standard JSON response.
98     *
99     * @param array<string, mixed> $payload Response data.
100     * @param int                  $status  HTTP status code.
101     * @return ResponseInterface Formatted PSR-7 response.
102     */
103    private function jsonResponse(array $payload, int $status = 200): ResponseInterface
104    {
105        $response = $this->factory->createResponse($status);
106        try {
107            $response->getBody()->write((string) json_encode($payload, JSON_THROW_ON_ERROR));
108        } catch (JsonException) {
109            $response->getBody()->write('{"success":false,"error":"JSON encoding error"}');
110        }
111
112        return $response->withHeader('Content-Type', 'application/json; charset=UTF-8');
113    }
114}