Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
LayoutApiController
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
3 / 3
5
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 positionBlocks
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 fetchPositionBlocksFromDb
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
3
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\Layout\Presentation\Api;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Shared\Infrastructure\Http\ApiResponseTrait;
12use PDO;
13use Psr\Http\Message\ResponseFactoryInterface;
14use Psr\Http\Message\ResponseInterface;
15use Psr\Http\Message\ServerRequestInterface;
16use Throwable;
17
18/**
19 * REST API Controller for Dynamic Layout Positions and Assigned Blocks.
20 *
21 * Exposes /api/v1/layout/positions/{code} to retrieve block configurations per theme position.
22 *
23 * @package App\Core\Layout\Presentation\Api
24 */
25final readonly class LayoutApiController
26{
27    use ApiResponseTrait;
28
29    /**
30     * LayoutApiController constructor.
31     *
32     * @param ResponseFactoryInterface $responseFactory PSR-7 Response factory.
33     * @param PDO                      $pdo             PDO database connection.
34     * @param string                   $tablePrefix     Database table prefix.
35     */
36    public function __construct(
37        private ResponseFactoryInterface $responseFactory,
38        private PDO                      $pdo,
39        private string                   $tablePrefix = 'a_'
40    ) {
41    }
42
43    /**
44     * Handles /api/v1/layout/positions/{code} request.
45     *
46     * @param ServerRequestInterface $request PSR-7 Server request.
47     * @param string                 $code    Position code (e.g. 'sidebar-left', 'header-top').
48     * @return ResponseInterface JSON API response.
49     */
50    public function positionBlocks(ServerRequestInterface $request, string $code): ResponseInterface
51    {
52        $params = $request->getQueryParams();
53        $theme = (string) ($params['theme'] ?? 'authenticated');
54
55        $blocks = $this->fetchPositionBlocksFromDb($code, $theme);
56
57        return $this->buildJsonResponse($this->responseFactory, [
58            'status'   => true,
59            'position' => $code,
60            'theme'    => $theme,
61            'blocks'   => $blocks,
62        ]);
63    }
64
65    /**
66     * Fetches assigned block class names for position directly from database.
67     *
68     * @param string $positionCode Position code.
69     * @param string $themeName    Theme name.
70     * @return array<int, string> List of block class names.
71     */
72    public function fetchPositionBlocksFromDb(string $positionCode, string $themeName): array
73    {
74        $positionsTable = $this->tablePrefix . 'core_layout_records';
75        $assignmentsTable = $this->tablePrefix . 'core_layout_assignments';
76
77        $sql = "SELECT a.block_class
78                FROM {$assignmentsTable} a
79                JOIN {$positionsTable} p ON a.position_id = p.id
80                WHERE p.position_code = :posCode
81                  AND p.theme_name = :themeName
82                  AND a.is_active = 1
83                ORDER BY a.sort_order ASC";
84
85        try {
86            $stmt = $this->pdo->prepare($sql);
87            $stmt->execute([
88                ':posCode'   => $positionCode,
89                ':themeName' => $themeName,
90            ]);
91
92            return $stmt->fetchAll(PDO::FETCH_COLUMN) ?: [];
93        } catch (Throwable) {
94            return [];
95        }
96    }
97}