1 | <?php |
||
2 | declare(strict_types=1); |
||
3 | |||
4 | namespace Stratadox\Hydration\Mapper\Instruction\Relation; |
||
5 | |||
6 | use Stratadox\Hydration\Mapper\Mapper; |
||
7 | use Stratadox\Hydration\Mapping\Property\Check; |
||
8 | use Stratadox\HydrationMapper\DefinesRelationships; |
||
9 | use Stratadox\HydrationMapper\FindsKeys; |
||
10 | use Stratadox\HydrationMapper\InstructsHowToMap; |
||
11 | use Stratadox\HydrationMapper\InvalidMapperConfiguration; |
||
12 | use Stratadox\HydrationMapper\RepresentsChoice; |
||
13 | use Stratadox\HydrationMapping\MapsProperty; |
||
14 | use Stratadox\Hydrator\Hydrates; |
||
15 | use Stratadox\Hydrator\OneOfTheseHydrators; |
||
16 | use Stratadox\Proxy\ProducesProxyLoaders; |
||
17 | use function assert; |
||
18 | use Stratadox\Specification\Contract\Satisfiable; |
||
19 | |||
20 | /** |
||
21 | * Defines a relationship with another class. |
||
22 | * |
||
23 | * @package Stratadox\Hydrate |
||
24 | * @author Stratadox |
||
25 | */ |
||
26 | abstract class Relationship implements DefinesRelationships |
||
27 | { |
||
28 | /** @var string */ |
||
29 | protected $class; |
||
30 | |||
31 | /** @var FindsKeys|null */ |
||
32 | protected $key; |
||
33 | |||
34 | /** @var string|null */ |
||
35 | protected $container; |
||
36 | |||
37 | /** @var ProducesProxyLoaders|null */ |
||
38 | protected $loader; |
||
39 | |||
40 | /** @var bool */ |
||
41 | protected $shouldNest = false; |
||
42 | |||
43 | /** @var (InstructsHowToMap|null)[] */ |
||
44 | protected $properties = []; |
||
45 | |||
46 | /** @var string|null */ |
||
47 | protected $decisionKey; |
||
48 | |||
49 | /** @var RepresentsChoice[] */ |
||
50 | protected $choices = []; |
||
51 | |||
52 | /** @var Satisfiable|null */ |
||
53 | protected $constraint; |
||
54 | |||
55 | private function __construct(string $class, FindsKeys $key = null) |
||
56 | { |
||
57 | $this->class = $class; |
||
58 | $this->key = $key; |
||
59 | } |
||
60 | |||
61 | /** |
||
62 | * Defines a new relationship with another class. |
||
63 | * |
||
64 | * @param string $class The fully qualified class name. |
||
65 | * @param FindsKeys|null $key The input array offset (optional) |
||
66 | * @return DefinesRelationships The relationship definition. |
||
67 | */ |
||
68 | public static function ofThe( |
||
69 | string $class, |
||
70 | FindsKeys $key = null |
||
71 | ): DefinesRelationships { |
||
72 | return new static($class, $key); |
||
73 | } |
||
74 | |||
75 | /** @inheritdoc */ |
||
76 | public function containedInA(string $container): DefinesRelationships |
||
77 | { |
||
78 | $inst = clone $this; |
||
79 | $inst->container = $container; |
||
80 | return $inst; |
||
81 | } |
||
82 | |||
83 | /** @inheritdoc */ |
||
84 | public function loadedBy(ProducesProxyLoaders $loader): DefinesRelationships |
||
85 | { |
||
86 | $inst = clone $this; |
||
87 | $inst->loader = $loader; |
||
88 | return $inst; |
||
89 | } |
||
90 | |||
91 | /** @inheritdoc */ |
||
92 | public function nested(): DefinesRelationships |
||
93 | { |
||
94 | $inst = clone $this; |
||
95 | $inst->shouldNest = true; |
||
96 | return $inst; |
||
97 | } |
||
98 | |||
99 | /** @inheritdoc */ |
||
100 | public function with( |
||
101 | string $property, |
||
102 | InstructsHowToMap $instruction = null |
||
103 | ): DefinesRelationships { |
||
104 | $inst = clone $this; |
||
105 | $inst->properties += [$property => $instruction]; |
||
106 | return $inst; |
||
107 | } |
||
108 | |||
109 | /** @inheritdoc */ |
||
110 | public function selectBy( |
||
111 | string $decisionKey, |
||
112 | array $choices |
||
113 | ): DefinesRelationships { |
||
114 | $inst = clone $this; |
||
115 | $inst->decisionKey = $decisionKey; |
||
116 | $inst->choices = $choices; |
||
117 | return $inst; |
||
118 | } |
||
119 | |||
120 | public function that(Satisfiable $constraint): InstructsHowToMap |
||
121 | { |
||
122 | $inst = clone $this; |
||
123 | $inst->constraint = $constraint; |
||
124 | return $inst; |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * Returns the key if one was provided, defaulting to the property name. |
||
129 | * |
||
130 | * @param string $property The property name to use as fallback. |
||
131 | * @return string The key to use as offset for the input data. |
||
132 | */ |
||
133 | protected function keyOr(string $property): string |
||
134 | { |
||
135 | return $this->key ? $this->key->find() : $property; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Produces a mapped hydrator according to the relationship configuration. |
||
140 | * |
||
141 | * @return Hydrates The hydrator for the relationship mapping. |
||
142 | * @throws InvalidMapperConfiguration |
||
143 | */ |
||
144 | protected function hydrator(): Hydrates |
||
145 | { |
||
146 | if (isset($this->decisionKey)) { |
||
147 | return $this->choiceHydrator(); |
||
148 | } |
||
149 | $mapped = Mapper::forThe($this->class); |
||
150 | foreach ($this->properties as $property => $instruction) { |
||
151 | $mapped = $mapped->property($property, $instruction); |
||
152 | } |
||
153 | return $mapped->finish(); |
||
154 | } |
||
155 | |||
156 | protected function addConstraintTo(MapsProperty $mapping): MapsProperty |
||
157 | { |
||
158 | if (isset($this->constraint)) { |
||
159 | $mapping = Check::that($this->constraint, $mapping); |
||
160 | } |
||
161 | return $mapping; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Produces a multiple-choice hydrator. |
||
166 | * |
||
167 | * @return Hydrates The adapter that selects the hydrator. |
||
168 | * @throws InvalidMapperConfiguration |
||
169 | */ |
||
170 | private function choiceHydrator(): Hydrates |
||
171 | { |
||
172 | assert(isset($this->decisionKey)); |
||
173 | return OneOfTheseHydrators::decideBasedOnThe( |
||
174 | $this->decisionKey, |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
175 | array_map(function (RepresentsChoice $choice): Hydrates { |
||
176 | return $choice->finish(); |
||
177 | }, $this->choices) |
||
178 | ); |
||
179 | } |
||
180 | } |
||
181 |