HasMany::oneProxyInThe()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.2
c 0
b 0
f 0
cc 4
eloc 12
nc 4
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Stratadox\Hydration\Mapper\Instruction\Relation;
5
6
use function class_exists;
7
use function class_implements;
8
use function in_array;
9
use Stratadox\Collection\Alterable;
10
use Stratadox\Hydration\Mapper\NoContainerAvailable;
11
use Stratadox\Hydration\Mapper\NoLoaderAvailable;
12
use Stratadox\Hydration\Mapper\NoSuchClass;
13
use Stratadox\Hydration\Mapping\Property\Relationship\HasManyNested;
14
use Stratadox\Hydration\Mapping\Property\Relationship\HasManyProxies;
15
use Stratadox\Hydration\Mapping\Property\Relationship\HasOneProxy;
16
use Stratadox\HydrationMapper\InvalidMapperConfiguration;
17
use Stratadox\HydrationMapping\MapsProperty;
18
use Stratadox\Hydrator\ArrayHydrator;
19
use Stratadox\Hydrator\Hydrates;
20
use Stratadox\Hydrator\SimpleHydrator;
21
use Stratadox\Hydrator\VariadicConstructor;
22
use Stratadox\Instantiator\CannotInstantiateThis;
23
use Stratadox\Proxy\AlterableCollectionEntryUpdaterFactory;
24
use Stratadox\Proxy\ArrayEntryUpdaterFactory;
25
use Stratadox\Proxy\ProducesOwnerUpdaters;
26
use Stratadox\Proxy\PropertyUpdaterFactory;
27
use Stratadox\Proxy\Proxy;
28
use Stratadox\Proxy\ProxyFactory;
29
30
/**
31
 * Indicates a polygamic relationship in the property.
32
 *
33
 * @package Stratadox\Hydrate
34
 * @author  Stratadox
35
 */
36
final class HasMany extends Relationship
37
{
38
    /** @inheritdoc */
39
    public function followFor(string $property): MapsProperty
40
    {
41
        if ($this->shouldNest) {
42
            return $this->manyNestedInThe($property);
43
        }
44
        if ($this->isImplementingThe(Proxy::class, $this->class)) {
45
            return $this->manyProxiesInThe($property);
46
        }
47
        return $this->oneProxyInThe($property);
48
    }
49
50
    /**
51
     * Maps an eagerly loaded collection from a nested data set.
52
     *
53
     * @param string $property The property that gets a nested eager relationship.
54
     * @return MapsProperty    The resulting property mapping.
55
     * @throws InvalidMapperConfiguration
56
     */
57
    private function manyNestedInThe(string $property): MapsProperty
58
    {
59
        if (null !== $this->container && !class_exists($this->container)) {
60
            throw NoSuchClass::couldNotLoadCollection($this->container);
61
        }
62
        return $this->addConstraintTo(HasManyNested::inPropertyWithDifferentKey(
63
            $property,
64
            $this->keyOr($property),
65
            $this->container(),
66
            $this->hydrator()
67
        ));
68
    }
69
70
    /**
71
     * Maps an extra lazily loaded collection as list of proxies.
72
     *
73
     * @param string $property The property that gets an extra lazy relationship.
74
     * @return MapsProperty    The resulting property mapping.
75
     * @throws InvalidMapperConfiguration
76
     */
77
    private function manyProxiesInThe(string $property): MapsProperty
78
    {
79
        if (null === $this->loader) {
80
            throw NoLoaderAvailable::whilstRequiredFor($this->class);
81
        }
82
        return $this->addConstraintTo(HasManyProxies::inPropertyWithDifferentKey(
83
            $property,
84
            $this->keyOr($property),
85
            $this->container(),
86
            ProxyFactory::fromThis(
87
                SimpleHydrator::forThe($this->class),
88
                $this->loader,
89
                $this->updaterFactory()
90
            )
91
        ));
92
    }
93
94
    /**
95
     * Maps a lazily loaded collection as a single proxy.
96
     *
97
     * @param string $property The property that gets a lazy relationship.
98
     * @return MapsProperty    The resulting property mapping.
99
     * @throws InvalidMapperConfiguration
100
     */
101
    private function oneProxyInThe(string $property): MapsProperty
102
    {
103
        if (null === $this->loader) {
104
            throw NoLoaderAvailable::whilstRequiredFor($this->class);
105
        }
106
        if (null === $this->container) {
107
            throw NoContainerAvailable::whilstRequiredFor($this->class);
108
        }
109
        try {
110
            return HasOneProxy::inProperty($property,
111
                ProxyFactory::fromThis(
112
                    SimpleHydrator::forThe($this->container),
113
                    $this->loader,
114
                    new PropertyUpdaterFactory
115
                )
116
            );
117
        } catch (CannotInstantiateThis $problem) {
118
            throw NoSuchClass::couldNotLoadCollection($this->container);
119
        }
120
    }
121
122
    /**
123
     * @return Hydrates The hydrator for the collection container.
124
     */
125
    private function container(): Hydrates
126
    {
127
        if (null !== $this->container) {
128
            return VariadicConstructor::forThe($this->container);
129
        }
130
        return ArrayHydrator::create();
131
    }
132
133
    /**
134
     * @return ProducesOwnerUpdaters The relevant updater factory.
135
     */
136
    private function updaterFactory(): ProducesOwnerUpdaters
137
    {
138
        if ($this->isImplementingThe(Alterable::class, $this->container)) {
139
            return new AlterableCollectionEntryUpdaterFactory;
140
        }
141
        return new ArrayEntryUpdaterFactory;
142
    }
143
144
    /**
145
     * @param string      $interface The interface name.
146
     * @param null|string $class     The class name.
147
     * @return bool                  Whether the class implements the interface.
148
     */
149
    private function isImplementingThe(string $interface, ?string $class): bool
150
    {
151
        return null !== $class
152
            && class_exists($class)
153
            && in_array($interface, class_implements($class));
154
    }
155
}
156