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

Decide   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 24
dl 0
loc 178
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A label() 0 3 1
A by() 0 8 1
A identifying() 0 7 1
A __construct() 0 5 1
A havingMany() 0 11 2
A mapThe() 0 5 1
A objects() 0 7 1
A which() 0 3 1
A basedOn() 0 8 1
C wiring() 0 35 8
A havingOne() 0 7 1
A identityColumns() 0 3 1
A hydrator() 0 9 2
A with() 0 7 2
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