Passed
Push — master ( 0d72e8...44be60 )
by Gabor
04:58
created

AbstractCoupler::__construct()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 14
cts 14
cp 1
rs 8.439
c 0
b 0
f 0
cc 5
eloc 19
nc 2
nop 3
crap 5
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2017 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
declare(strict_types = 1);
13
14
namespace WebHemi\Data\Coupler;
15
16
use InvalidArgumentException;
17
use RuntimeException;
18
use WebHemi\Data\CouplerInterface;
19
use WebHemi\Data\EntityInterface;
20
use WebHemi\Data\ConnectorInterface;
21
22
/**
23
 * Class AbstractCoupler.
24
 */
25
abstract class AbstractCoupler implements CouplerInterface
26
{
27
    /** @var ConnectorInterface */
28
    private $connector;
29
    /** @var array<EntityInterface> */
30
    protected $dataEntityPrototypes = [];
31
    /** @var string */
32
    protected $connectorIdKey;
33
    /** @var string */
34
    protected $connectorDataGroup;
35
    /** @var array */
36
    protected $dependentDataGroups;
37
38
    /**
39
     * AbstractCoupler constructor.
40
     *
41
     * @param ConnectorInterface $connector
42
     * @param EntityInterface    $dataEntityPrototypeA
43
     * @param EntityInterface    $dataEntityPrototypeB
44
     */
45 8
    public function __construct(
46
        ConnectorInterface $connector,
47
        EntityInterface $dataEntityPrototypeA,
48
        EntityInterface $dataEntityPrototypeB
49
    ) {
50 8
        $entityClassA = get_class($dataEntityPrototypeA);
51 8
        $entityClassB = get_class($dataEntityPrototypeB);
52
53 8
        if (!isset($this->dependentDataGroups[$entityClassA])
54 8
            || !isset($this->dependentDataGroups[$entityClassB])
55 8
            || (count(array_keys($this->dependentDataGroups)) == 2 && $entityClassA == $entityClassB)
56
        ) {
57 4
            throw new InvalidArgumentException(
58
                sprintf(
59 4
                    'This coupler requires data entity instances from the following classes: %s; %s and %s are given.',
60 4
                    implode(', ', array_keys($this->dependentDataGroups)),
61
                    $entityClassA,
62
                    $entityClassB
63
                ),
64 4
                1000
65
            );
66
        }
67
68 8
        $this->connector = $connector;
69 8
        $this->dataEntityPrototypes[$entityClassA] = $dataEntityPrototypeA;
70 8
        $this->dataEntityPrototypes[$entityClassB] = $dataEntityPrototypeB;
71 8
    }
72
73
    /**
74
     * Returns the DataAdapter instance.
75
     *
76
     * @return ConnectorInterface
77
     */
78 6
    public function getConnector() : ConnectorInterface
79
    {
80 6
        return $this->connector;
81
    }
82
83
    /**
84
     * Gets all the entities those are depending from the given entity.
85
     *
86
     * @param EntityInterface $dataEntity
87
     * @throws RuntimeException
88
     * @return array<EntityInterface>
89
     */
90 5
    public function getEntityDependencies(EntityInterface $dataEntity) : array
91
    {
92 5
        $entityClass = get_class($dataEntity);
93 5
        if (!isset($this->dataEntityPrototypes[$entityClass])) {
94 1
            throw new RuntimeException(
95 1
                sprintf('Cannot use this coupler class to find dependencies for %s.', $entityClass),
96 1
                1001
97
            );
98
        }
99
100 5
        $entityList = [];
101 5
        $dataList = $this->getEntityDataSet($dataEntity);
102
103 5
        foreach ($dataList as $entityData) {
104 5
            $entityList[] = $this->getDependingEntity($dataEntity, $entityData);
105
        }
106
107 5
        return $entityList;
108
    }
109
110
    /**
111
     * Sets dependency for the entities
112
     *
113
     * @param EntityInterface $dataEntityA
114
     * @param EntityInterface $dataEntityB
115
     * @return int The ID of the saved entity in the storage
116
     */
117 1
    public function setDependency(EntityInterface $dataEntityA, EntityInterface $dataEntityB) : int
118
    {
119 1
        $entityClassA = get_class($dataEntityA);
120 1
        if (!isset($this->dataEntityPrototypes[$entityClassA])) {
121 1
            throw new InvalidArgumentException(sprintf('Cannot use this coupler class for %s.', $entityClassA), 1002);
122
        }
123
124 1
        $entityClassB = get_class($dataEntityB);
125 1
        if (!isset($this->dataEntityPrototypes[$entityClassB])) {
126 1
            throw new InvalidArgumentException(sprintf('Cannot use this coupler class for %s.', $entityClassB), 1003);
127
        }
128
129 1
        if ($entityClassA == $entityClassB) {
130 1
            throw new InvalidArgumentException(
131 1
                sprintf('Cannot set dependency for the same type of entity %s.', $entityClassB),
132 1
                1004
133
            );
134
        }
135
136
        $data = [
137 1
            $this->dependentDataGroups[$entityClassA]['source_key'] => $dataEntityA->getKeyData(),
138 1
            $this->dependentDataGroups[$entityClassB]['source_key'] => $dataEntityB->getKeyData(),
139
        ];
140
141
        // Point the data adapter to the connector group
142 1
        return $this->getConnector()
143 1
            ->setDataGroup($this->connectorDataGroup)
144 1
            ->setIdKey($this->connectorIdKey)
145 1
            ->saveData(null, $data);
146
    }
147
148
    /**
149
     * Gets a DataEntityInterface instance from the provided data according to the reference entity.
150
     *
151
     * @param EntityInterface $referenceEntity
152
     * @param array           $entityData
153
     * @return EntityInterface
154
     */
155
    abstract protected function getDependingEntity(
156
        EntityInterface $referenceEntity,
157
        array $entityData
158
    ) : EntityInterface;
159
160
    /**
161
     * Returns a new instance of the required entity.
162
     *
163
     * @param string $entityClassName
164
     * @throws RuntimeException
165
     * @return EntityInterface
166
     */
167 6
    protected function getNewEntityInstance(string $entityClassName) : EntityInterface
168
    {
169 6
        return clone $this->dataEntityPrototypes[$entityClassName];
170
    }
171
172
    /**
173
     * Gets raw depending entity data list for the given entity.
174
     *
175
     * @param EntityInterface $dataEntity
176
     * @return array
177
     */
178 4
    protected function getEntityDataSet(EntityInterface $dataEntity) : array
179
    {
180 4
        $entityClassName = get_class($dataEntity);
181 4
        $entityDataSet = [];
182 4
        $identifiers = [];
183
184 4
        $this->getConnector()->setDataGroup($this->connectorDataGroup)
185 4
            ->setIdKey($this->connectorIdKey);
186
187 4
        $dataList = $this->getConnector()->getDataSet([
188 4
            $this->dependentDataGroups[$entityClassName]['source_key'].' = ?' => $dataEntity->getKeyData()
189
        ]);
190
191 4
        foreach ($dataList as $rowData) {
192 4
            $identifiers[] = $rowData[$this->dependentDataGroups[$entityClassName]['connector_key']];
193
        }
194
195 4
        if (!empty($identifiers)) {
196 4
            $this->getConnector()->setDataGroup($this->dependentDataGroups[$entityClassName]['depending_group'])
197 4
                ->setIdKey($this->dependentDataGroups[$entityClassName]['depending_id_key']);
198
199 4
            $entityDataSet = $this->getConnector()->getDataSet([
200 4
                $this->dependentDataGroups[$entityClassName]['depending_id_key'].' IN (?)' => $identifiers
201
            ]);
202
        }
203
204 4
        return $entityDataSet;
205
    }
206
}
207