Passed
Push — master ( 97f73d...f32c79 )
by Gabor
08:10
created

AbstractDataCoupler::getEntityDependencies()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

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