Completed
Push — master ( 9e9e7e...b5260b )
by Jesse
02:00
created

Decide::wiring()   C

Complexity

Conditions 8
Paths 36

Size

Total Lines 35
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 22
nc 36
nop 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Stratadox\TableLoader;
5
6
use function array_unshift;
7
use function assert;
8
use function count;
9
use Countable;
10
use Stratadox\HydrationMapper\InvalidMapperConfiguration;
11
use Stratadox\Hydrator\ArrayHydrator;
12
use Stratadox\Hydrator\Hydrates;
13
use Stratadox\Hydrator\OneOfTheseHydrators;
14
use Stratadox\Hydrator\VariadicConstructor;
15
use Stratadox\Instantiator\CannotInstantiateThis;
16
17
final class Decide implements DefinesMultipleClassMapping
18
{
19
    private $label;
20
    private $ownId = [];
21
    private $identityColumnsFor;
22
    private $decisionKey;
23
    /** @var LoadsWhenTriggered[] */
24
    private $choices = [];
25
    /** @var MakesConnections[] */
26
    private $relation = [];
27
28
    private function __construct(string $label)
29
    {
30
        $this->label = $label;
31
        $this->ownId = ['id'];
32
        $this->identityColumnsFor[$label] = [$label . '_type', $label . '_id'];
33
    }
34
35
    public static function which(string $label): DefinesMultipleClassMapping
36
    {
37
        return new self($label);
38
    }
39
40
    /** @inheritdoc */
41
    public function basedOn(
42
        string $key,
43
        LoadsWhenTriggered ...$choices
44
    ): DefinesMultipleClassMapping {
45
        $new = clone $this;
46
        $new->decisionKey = $key;
47
        $new->choices = $choices;
48
        return $new;
49
    }
50
51
    /** @inheritdoc */
52
    public function by(string ...$columns): DefinesMultipleClassMapping
53
    {
54
        $label = $this->label;
55
        $new = clone $this;
56
        $new->ownId = $columns;
57
        array_unshift($columns, $this->decisionKey);
58
        $new->identityColumnsFor[$label] = $this->mapThe($columns, $label . '_');
59
        return $new;
60
    }
61
62
    /** @inheritdoc */
63
    public function havingOne(
64
        string $property,
65
        string $label
66
    ): DefinesObjectMapping {
67
        $new = clone $this;
68
        $new->relation[$label] = HasOne::in($property);
69
        return $new;
70
    }
71
72
    /** @inheritdoc */
73
    public function havingMany(
74
        string $property,
75
        string $label,
76
        string $collectionClass = null
77
    ): DefinesObjectMapping {
78
        $hydrator = $collectionClass ?
79
            VariadicConstructor::forThe($collectionClass) :
80
            ArrayHydrator::create();
81
        $new = clone $this;
82
        $new->relation[$label] = HasMany::in($property, $hydrator);
83
        return $new;
84
    }
85
86
    /** @inheritdoc */
87
    public function with(array $properties): DefinesMultipleClassMapping
88
    {
89
        $new = clone $this;
90
        foreach ($this->choices as $i => $choice) {
91
            $new->choices[$i] = $choice->with($properties);
92
        }
93
        return $new;
94
    }
95
96
    /** @inheritdoc */
97
    public function identifying(
98
        string $label,
99
        string ...$columns
100
    ): DefinesObjectMapping {
101
        $new = clone $this;
102
        $new->identityColumnsFor[$label] = $columns;
103
        return $new;
104
    }
105
106
    /** @inheritdoc */
107
    public function label(): string
108
    {
109
        return $this->label;
110
    }
111
112
    /** @inheritdoc */
113
    public function identityColumns(): array
114
    {
115
        return $this->identityColumnsFor[$this->label];
116
    }
117
118
    /** @inheritdoc */
119
    public function objects(): MakesObjects
120
    {
121
        assert(isset($this->decisionKey));
122
        return Objects::producedByThis(
123
            $this->hydrator(),
124
            Prefixed::with($this->label),
125
            Identified::by(...$this->ownId)->andForLoading($this->decisionKey)
126
        );
127
    }
128
129
    /** @inheritdoc */
130
    public function wiring(): WiresObjects
131
    {
132
        // @todo extract methods
133
        $ownLabel = $this->label;
134
        $ownId = $this->identityColumnsFor[$ownLabel];
135
        $wires = [];
136
        foreach ($this->relation as $otherLabel => $connectThem) {
137
            //@todo $this->mustKnowTheIdentityColumnsFor($otherLabel);
138
            $otherId = $this->identityColumnsFor[$otherLabel];
139
            $wires[] = Wire::it(
140
                From::the($ownLabel, Identified::by(...$ownId)),
141
                To::the($otherLabel, Identified::by(...$otherId)),
142
                $connectThem
143
            );
144
        }
145
        /** @var LoadsWhenTriggered[] $choices */
146
        $choices = [];
147
        foreach ($this->choices as $choice) {
148
            foreach ($this->identityColumnsFor as $label => $columns) {
149
                $choice = $choice->identifying($label, ...$columns);
150
            }
151
            $choices[] = $choice->labeled($this->label);
152
153
        }
154
        foreach ($choices as $choice) {
155
            $wiring = $choice->wiring();
156
            if ($wiring instanceof Countable && count($wiring) === 0) {
157
                continue;
158
            }
159
            $wires[] = $wiring;
160
        }
161
        if (count($wires) === 1) {
162
            return $wires[0];
163
        }
164
        return Wired::together(...$wires);
165
    }
166
167
    /**
168
     * @return Hydrates
169
     * @throws InvalidMapperConfiguration
170
     * @throws CannotInstantiateThis
171
     */
172
    private function hydrator(): Hydrates
173
    {
174
        $choices = [];
175
        foreach ($this->choices as $choice) {
176
            $choices[$choice->decisionTrigger()] = $choice->hydrator();
177
        }
178
        return OneOfTheseHydrators::decideBasedOnThe(
179
            $this->decisionKey,
180
            $choices
181
        );
182
    }
183
184
    /**
185
     * Prepends the identifying columns with a label.
186
     *
187
     * @param array $columns
188
     * @return array
189
     */
190
    private function mapThe(array $columns, string $prefix): array
191
    {
192
        return array_map(function(string $column) use ($prefix): string {
193
            return $prefix . $column;
194
        }, $columns);
195
    }
196
}
197