Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 25 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
| PositionManager | |
0.00% |
0 / 25 |
|
0.00% |
0 / 2 |
72 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| renderPosition | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
56 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace App\Core\Layout; |
| 6 | |
| 7 | use PDO; |
| 8 | use Psr\Container\ContainerInterface; |
| 9 | use Throwable; |
| 10 | |
| 11 | /** |
| 12 | * Layout Position and Block Manager. |
| 13 | * |
| 14 | * Fetches assigned blocks for a specific theme position from database |
| 15 | * and securely renders their concatenated HTML output. |
| 16 | * |
| 17 | * @package App\Core\Layout |
| 18 | */ |
| 19 | final readonly class PositionManager |
| 20 | { |
| 21 | /** |
| 22 | * PositionManager constructor. |
| 23 | * |
| 24 | * @param PDO $pdo SQL Database Connection PDO instance. |
| 25 | * @param ContainerInterface $container PSR-11 DI Container. |
| 26 | * @param string $tablePrefix Database table prefix (e.g. 'a_'). |
| 27 | */ |
| 28 | public function __construct( |
| 29 | private PDO $pdo, |
| 30 | private ContainerInterface $container, |
| 31 | private string $tablePrefix = 'a_' |
| 32 | ) { |
| 33 | } |
| 34 | |
| 35 | /** |
| 36 | * Renders all active blocks assigned to a position within a theme. |
| 37 | * |
| 38 | * @param string $positionCode Position code (e.g. 'sidebar-left', 'header-top'). |
| 39 | * @param string $themeName Theme name (e.g. 'guest', 'authenticated'). |
| 40 | * @return string Accumulated rendered HTML string of all blocks. |
| 41 | */ |
| 42 | public function renderPosition(string $positionCode, string $themeName): string |
| 43 | { |
| 44 | $positionsTable = $this->tablePrefix . 'core_layout_records'; |
| 45 | $assignmentsTable = $this->tablePrefix . 'core_layout_assignments'; |
| 46 | |
| 47 | $sql = "SELECT a.block_class |
| 48 | FROM {$assignmentsTable} a |
| 49 | JOIN {$positionsTable} p ON a.position_id = p.id |
| 50 | WHERE p.position_code = :posCode |
| 51 | AND p.theme_name = :themeName |
| 52 | AND a.is_active = 1 |
| 53 | ORDER BY a.sort_order ASC"; |
| 54 | |
| 55 | $stmt = $this->pdo->prepare($sql); |
| 56 | $stmt->execute([ |
| 57 | ':posCode' => $positionCode, |
| 58 | ':themeName' => $themeName, |
| 59 | ]); |
| 60 | |
| 61 | $blocks = $stmt->fetchAll(PDO::FETCH_COLUMN); |
| 62 | if (empty($blocks)) { |
| 63 | return ''; |
| 64 | } |
| 65 | |
| 66 | $html = ''; |
| 67 | foreach ($blocks as $blockClass) { |
| 68 | if (!is_string($blockClass) || !class_exists($blockClass)) { |
| 69 | continue; |
| 70 | } |
| 71 | |
| 72 | try { |
| 73 | /** @var mixed $block */ |
| 74 | $block = $this->container->get($blockClass); |
| 75 | if ($block instanceof BlockInterface) { |
| 76 | $html .= $block->render(); |
| 77 | } |
| 78 | } catch (Throwable) { |
| 79 | // Silently skip broken blocks to prevent page crash |
| 80 | continue; |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | return $html; |
| 85 | } |
| 86 | } |