Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
TemplateBlock
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
5 / 5
18
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
 fromArray
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
7
 extractArray
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 extractChildren
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 toArray
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
2
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\Template\Domain\Model;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11/**
12 * Immutable Template Block Value Object for Modular Template Building.
13 *
14 * Encapsulates block type, 2D grid coordinates (x, y, w, h), customizable properties,
15 * nested children, and inline visual styling rules.
16 *
17 * @package App\Core\Template\Domain\Model
18 */
19final readonly class TemplateBlock
20{
21    /**
22     * TemplateBlock constructor.
23     *
24     * @param string                $id       Unique block node ID.
25     * @param string                $type     Block type identifier (e.g. brand_header, rich_text).
26     * @param int                   $x        Horizontal grid position (0 to 11).
27     * @param int                   $y        Vertical grid row coordinate.
28     * @param int                   $w        Width in grid columns (1 to 12).
29     * @param int                   $h        Height in grid rows.
30     * @param array<string, mixed>  $props    Block configuration properties.
31     * @param array<int, self>      $children Nested child blocks.
32     * @param array<string, string> $styles   Custom styling key-value rules.
33     */
34    public function __construct(
35        public string $id,
36        public string $type,
37        public int $x = 0,
38        public int $y = 0,
39        public int $w = 12,
40        public int $h = 2,
41        public array $props = [],
42        public array $children = [],
43        public array $styles = []
44    ) {
45    }
46
47    /**
48     * Instantiates TemplateBlock from an associative array.
49     *
50     * @param array<string, mixed> $data Serialized block dictionary.
51     * @return self Populated TemplateBlock instance.
52     */
53    public static function fromArray(array $data): self
54    {
55        $id = isset($data['id']) && (string)$data['id'] !== ''
56            ? (string)$data['id']
57            : 'blk_' . bin2hex(random_bytes(4));
58        $type = (string)($data['type'] ?? 'rich_text_box');
59        $x = isset($data['x']) ? max(0, (int)$data['x']) : 0;
60        $y = isset($data['y']) ? max(0, (int)$data['y']) : 0;
61        $w = isset($data['w']) ? max(1, min(12, (int)$data['w'])) : 12;
62        $h = isset($data['h']) ? max(1, (int)$data['h']) : 2;
63        $props = self::extractArray($data, 'props');
64        $styles = self::extractArray($data, 'styles');
65        $children = self::extractChildren($data);
66
67        return new self($id, $type, $x, $y, $w, $h, $props, $children, $styles);
68    }
69
70    /**
71     * @param array<string, mixed> $data
72     * @param string               $key
73     * @return array<string, mixed>
74     */
75    private static function extractArray(array $data, string $key): array
76    {
77        return isset($data[$key]) && is_array($data[$key]) ? $data[$key] : [];
78    }
79
80    /**
81     * @param array<string, mixed> $data
82     * @return array<int, self>
83     */
84    private static function extractChildren(array $data): array
85    {
86        if (!isset($data['children']) || !is_array($data['children'])) {
87            return [];
88        }
89
90        $children = [];
91        foreach ($data['children'] as $childData) {
92            if (is_array($childData)) {
93                $children[] = self::fromArray($childData);
94            }
95        }
96
97        return $children;
98    }
99
100    /**
101     * Serializes block into nested array structure for JSON export.
102     *
103     * @return array<string, mixed> Array representation of the block tree.
104     */
105    public function toArray(): array
106    {
107        $serializedChildren = [];
108        foreach ($this->children as $child) {
109            $serializedChildren[] = $child->toArray();
110        }
111
112        return [
113            'id'       => $this->id,
114            'type'     => $this->type,
115            'x'        => $this->x,
116            'y'        => $this->y,
117            'w'        => $this->w,
118            'h'        => $this->h,
119            'props'    => $this->props,
120            'children' => $serializedChildren,
121            'styles'   => $this->styles,
122        ];
123    }
124}