Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.84% covered (warning)
86.84%
33 / 38
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Relation1mMetadata
89.19% covered (warning)
89.19%
33 / 37
75.00% covered (warning)
75.00%
3 / 4
14.25
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
 hasAction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllowedActions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromRow
88.24% covered (warning)
88.24%
30 / 34
0.00% covered (danger)
0.00%
0 / 1
11.20
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\Engine\Domain\Model;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11/**
12 * 1:M Relation Metadata Value Object.
13 *
14 * Immutable value object representing a 1:M entity relation configuration
15 * from a_core_relation_1m_records. Defines source and target module connections,
16 * foreign key mapping, sort order, and active status.
17 *
18 * @package App\Core\Engine\Domain\Model
19 */
20final readonly class Relation1mMetadata
21{
22    /**
23     * Relation1mMetadata constructor.
24     *
25     * @param int                  $id             Relation primary key.
26     * @param string               $name           Machine name identifier (e.g. 'module_fields').
27     * @param string               $label          Display label (e.g. 'Pola').
28     * @param int                  $sourceModuleId Source module ID (parent).
29     * @param int                  $targetModuleId Target module ID (related child).
30     * @param string               $targetFieldKey Foreign key field in target module table.
31     * @param string               $relationType   Relation type (default '1:M').
32     * @param bool                 $isActive       Whether this relation is active.
33     * @param int                  $sortOrder      Ordering position in tabs.
34     * @param array<int, string>   $visibleFields  Configured visible field keys list.
35     * @param string|null          $sourceModuleName Optional joined source module name.
36     * @param string|null          $targetModuleName Optional joined target module name.
37     * @param string|null          $targetModuleRoute Optional joined target module route.
38     * @param string|null          $targetModuleIcon  Optional joined target module icon.
39     */
40    public function __construct(
41        public readonly int     $id,
42        public readonly string  $name,
43        public readonly string  $label,
44        public readonly int     $sourceModuleId,
45        public readonly int     $targetModuleId,
46        public readonly string  $targetFieldKey,
47        public readonly string  $relationType = '1:M',
48        public readonly bool    $isActive = true,
49        public readonly int     $sortOrder = 10,
50        public readonly array   $visibleFields = [],
51        public readonly ?string $sourceModuleName = null,
52        public readonly ?string $targetModuleName = null,
53        public readonly ?string $targetModuleRoute = null,
54        public readonly ?string $targetModuleIcon = null,
55        public readonly ?array  $targetModuleIds = null,
56        public readonly array   $allowedActions = ['create', 'refresh'],
57    ) {
58    }
59
60    /**
61     * Checks if a specific action is permitted in this relation.
62     *
63     * @param string $action Action key to test ('create', 'select', 'refresh', 'pdf').
64     * @return bool True if action is allowed.
65     */
66    public function hasAction(string $action): bool
67    {
68        return in_array($action, $this->allowedActions, true);
69    }
70
71    /**
72     * Returns the array of allowed action identifiers.
73     *
74     * @return array<int, string>
75     */
76    public function getAllowedActions(): array
77    {
78        return $this->allowedActions;
79    }
80
81    /**
82     * Creates a Relation1mMetadata instance from a raw database row array.
83     *
84     * @param array<string, mixed> $row Raw database row from a_core_relation_1m_records.
85     * @return self Hydrated Relation1mMetadata value object.
86     */
87    public static function fromRow(array $row): self
88    {
89        $rawVisible = $row['visible_fields'] ?? '[]';
90        $visibleFields = is_string($rawVisible)
91            ? (array) (json_decode($rawVisible, true) ?? [])
92            : (array) $rawVisible;
93
94        $rawTargetMods = $row['target_module_ids'] ?? null;
95        $targetModuleIds = null;
96        if ($rawTargetMods !== null) {
97            $targetModuleIds = is_string($rawTargetMods)
98                ? (array) (json_decode($rawTargetMods, true) ?? [])
99                : (array) $rawTargetMods;
100        }
101
102        $rawAllowed = $row['allowed_actions'] ?? null;
103        $allowedActions = ['create', 'refresh'];
104        if ($rawAllowed !== null) {
105            $decoded = is_string($rawAllowed) ? json_decode($rawAllowed, true) : $rawAllowed;
106            if (is_array($decoded)) {
107                $allowedActions = array_values(array_filter(array_map('strval', $decoded)));
108            }
109        }
110
111        return new self(
112            id:                (int)    $row['id'],
113            name:              (string) $row['name'],
114            label:             (string) $row['label'],
115            sourceModuleId:    (int)    $row['source_module_id'],
116            targetModuleId:    (int)    $row['target_module_id'],
117            targetFieldKey:    (string) $row['target_field_key'],
118            relationType:      (string) ($row['relation_type'] ?? '1:M'),
119            isActive:          (bool)   ($row['is_active']     ?? true),
120            sortOrder:         (int)    ($row['sort_order']    ?? 10),
121            visibleFields:     array_values(array_filter(array_map('strval', $visibleFields))),
122            sourceModuleName:  isset($row['source_module_name']) ? (string) $row['source_module_name'] : null,
123            targetModuleName:  isset($row['target_module_name']) ? (string) $row['target_module_name'] : null,
124            targetModuleRoute: isset($row['target_module_route']) ? (string) $row['target_module_route'] : null,
125            targetModuleIcon:  isset($row['target_module_icon']) ? (string) $row['target_module_icon'] : null,
126            targetModuleIds:   $targetModuleIds,
127            allowedActions:    $allowedActions,
128        );
129    }
130}