• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

diego-ninja / granite / 16608963009

29 Jul 2025 10:37PM UTC coverage: 50.423%. First build
16608963009

Pull #5

github

web-flow
Merge 43d8840d7 into 6a6caca51
Pull Request #5: code: adds phpstan level max, pint with per and github actions

321 of 632 new or added lines in 77 files covered. (50.79%)

1132 of 2245 relevant lines covered (50.42%)

9.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Mapping/BidirectionalTypeMapping.php
1
<?php
2

3
namespace Ninja\Granite\Mapping;
4

5
use Ninja\Granite\Mapping\Contracts\MappingStorage;
6
use Ninja\Granite\Mapping\Exceptions\MappingException;
7
use RuntimeException;
8

9
/**
10
 * Manages bidirectional mapping between two types.
11
 */
12
final class BidirectionalTypeMapping
13
{
14
    /**
15
     * Forward mapping (typeA to typeB).
16
     */
17
    private TypeMapping $forwardMapping;
18

19
    /**
20
     * Reverse mapping (typeB to typeA).
21
     */
22
    private TypeMapping $reverseMapping;
23

24
    /**
25
     * Forward member mappings.
26
     *
27
     * @var array<string, string>
28
     */
29
    private array $forwardMemberMappings = [];
30

31
    /**
32
     * Reverse member mappings.
33
     *
34
     * @var array<string, string>
35
     */
36
    private array $reverseMemberMappings = [];
37

38
    /**
39
     * Whether the mapping is sealed.
40
     */
41
    private bool $sealed = false;
42

43
    /**
44
     * Constructor.
45
     *
46
     * @param MappingStorage $storage Mapping storage
47
     * @param class-string $typeA First type name
48
     * @param class-string $typeB Second type name
49
     */
50
    public function __construct(
×
51
        private readonly MappingStorage $storage,
52
        private readonly string $typeA,
53
        private readonly string $typeB,
54
    ) {
55
        // Create basic mappings in both directions
56
        $this->forwardMapping = new TypeMapping($storage, $typeA, $typeB);
×
57
        $this->reverseMapping = new TypeMapping($storage, $typeB, $typeA);
×
58
    }
59

60
    /**
61
     * Configure bidirectional mapping for a property pair.
62
     *
63
     * @param string $propertyA Property name in typeA
64
     * @param string $propertyB Property name in typeB
65
     * @return $this For method chaining
66
     * @throws RuntimeException If the mapping is already sealed
67
     */
68
    public function forMembers(string $propertyA, string $propertyB): self
×
69
    {
70
        if ($this->sealed) {
×
71
            throw new RuntimeException("Cannot modify bidirectional mapping after it has been sealed");
×
72
        }
73

74
        // Store the mapping configuration
75
        $this->forwardMemberMappings[$propertyA] = $propertyB;
×
76
        $this->reverseMemberMappings[$propertyB] = $propertyA;
×
77

78
        return $this;
×
79
    }
80

81
    /**
82
     * Configure multiple property pairs at once.
83
     *
84
     * @param array<string, string> $propertyPairs Associative array of propertyA => propertyB pairs
85
     * @return $this For method chaining
86
     * @throws RuntimeException If the mapping is already sealed
87
     */
88
    public function forMemberPairs(array $propertyPairs): self
×
89
    {
90
        if ($this->sealed) {
×
91
            throw new RuntimeException("Cannot modify bidirectional mapping after it has been sealed");
×
92
        }
93

94
        foreach ($propertyPairs as $propertyA => $propertyB) {
×
95
            $this->forwardMemberMappings[$propertyA] = $propertyB;
×
96
            $this->reverseMemberMappings[$propertyB] = $propertyA;
×
97
        }
98

99
        return $this;
×
100
    }
101

102
    /**
103
     * Apply a one-way mapping from typeA to typeB.
104
     *
105
     * @param string $propertyB Destination property in typeB
106
     * @param callable $configuration Mapping configuration function
107
     * @return $this For method chaining
108
     * @throws RuntimeException|MappingException If the mapping is already sealed
109
     */
110
    public function forForwardMember(string $propertyB, callable $configuration): self
×
111
    {
112
        if ($this->sealed) {
×
113
            throw new RuntimeException("Cannot modify bidirectional mapping after it has been sealed");
×
114
        }
115

116
        // Apply configuration to forward mapping only
117
        $this->forwardMapping->forMember($propertyB, $configuration);
×
118

119
        return $this;
×
120
    }
121

122
    /**
123
     * Apply a one-way mapping from typeB to typeA.
124
     *
125
     * @param string $propertyA Destination property in typeA
126
     * @param callable $configuration Mapping configuration function
127
     * @return $this For method chaining
128
     * @throws RuntimeException|MappingException If the mapping is already sealed
129
     */
130
    public function forReverseMember(string $propertyA, callable $configuration): self
×
131
    {
132
        if ($this->sealed) {
×
133
            throw new RuntimeException("Cannot modify bidirectional mapping after it has been sealed");
×
134
        }
135

136
        // Apply configuration to reverse mapping only
137
        $this->reverseMapping->forMember($propertyA, $configuration);
×
138

139
        return $this;
×
140
    }
141

142
    /**
143
     * Validate and finalize the bidirectional mapping.
144
     *
145
     * @return $this For method chaining
146
     * @throws MappingException
147
     */
148
    public function seal(): self
×
149
    {
150
        if ($this->sealed) {
×
151
            return $this; // Already sealed
×
152
        }
153

154
        // Apply all bidirectional mappings
155
        foreach ($this->forwardMemberMappings as $propertyA => $propertyB) {
×
156
            $this->forwardMapping->forMember($propertyB, fn($m) => $m->mapFrom($propertyA));
×
157
            $this->reverseMapping->forMember($propertyA, fn($m) => $m->mapFrom($propertyB));
×
158
        }
159

160
        // Seal both mappings
161
        $this->forwardMapping->seal();
×
162
        $this->reverseMapping->seal();
×
163

164
        $this->sealed = true;
×
165

166
        return $this;
×
167
    }
168

169
    /**
170
     * Get the forward mapping (typeA to typeB).
171
     *
172
     * @return TypeMapping Forward mapping
173
     */
174
    public function getForwardMapping(): TypeMapping
×
175
    {
176
        return $this->forwardMapping;
×
177
    }
178

179
    /**
180
     * Get the reverse mapping (typeB to typeA).
181
     *
182
     * @return TypeMapping Reverse mapping
183
     */
184
    public function getReverseMapping(): TypeMapping
×
185
    {
186
        return $this->reverseMapping;
×
187
    }
188

189
    /**
190
     * Get reverse member mappings.
191
     *
192
     * @return array<string, string> Reverse member mappings
193
     */
NEW
194
    public function getReverseMemberMappings(): array
×
195
    {
NEW
196
        return $this->reverseMemberMappings;
×
197
    }
198

199
    /**
200
     * Get mapping storage.
201
     *
202
     * @return MappingStorage Mapping storage
203
     */
NEW
204
    public function getStorage(): MappingStorage
×
205
    {
NEW
206
        return $this->storage;
×
207
    }
208

209
    /**
210
     * Get type A.
211
     *
212
     * @return string Type A name
213
     */
NEW
214
    public function getTypeA(): string
×
215
    {
NEW
216
        return $this->typeA;
×
217
    }
218

219
    /**
220
     * Get type B.
221
     *
222
     * @return string Type B name
223
     */
NEW
224
    public function getTypeB(): string
×
225
    {
NEW
226
        return $this->typeB;
×
227
    }
228
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc