Passed
Push — develop ( 2402e6...7bcf99 )
by Anton
15:02
created

Relations::findRelations()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 62
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 28
nc 3
nop 3
dl 0
loc 62
ccs 0
cts 28
cp 0
crap 12
rs 9.472
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Bluz Framework Component
4
 *
5
 * @copyright Bluz PHP Team
6
 * @link      https://github.com/bluzphp/framework
7
 */
8
9
declare(strict_types=1);
10
11
namespace Bluz\Db;
12
13
use Bluz\Db\Exception\RelationNotFoundException;
14
15
/**
16
 * Relations map of Db tables
17
 *
18
 * @package  Bluz\Db
19
 * @author   Anton Shevchuk
20
 */
21
class Relations
22
{
23
    /**
24
     * Relation stack, i.e.
25
     * <code>
26
     *     [
27
     *         'Model1:Model2' => ['Model1'=>'foreignKey', 'Model2'=>'primaryKey'],
28
     *         'Pages:Users' => ['Pages'=>'userId', 'Users'=>'id'],
29
     *         'PagesTags:Pages' => ['PagesTags'=>'pageId', 'Pages'=>'id'],
30
     *         'PagesTags:Tags' => ['PagesTags'=>'tagId', 'Tags'=>'id'],
31
     *         'Pages:Tags' => ['PagesTags'],
32
     *     ]
33
     * </code>
34
     *
35
     * @var array
36
     */
37
    protected static $relations;
38
39
    /**
40
     * Class map, i.e.
41
     * <code>
42
     *     [
43
     *         'Pages' => '\Application\Pages\Table',
44
     *         'Users' => '\Application\Users\Table',
45
     *     ]
46
     * </code>
47
     *
48
     * @var array
49
     */
50
    protected static $modelClassMap;
51
52
    /**
53
     * Setup relation between two models
54
     *
55
     * @param  string $modelOne
56
     * @param  string $keyOne
57
     * @param  string $modelTwo
58
     * @param  string $keyTwo
59
     *
60
     * @return void
61
     */
62
    public static function setRelation($modelOne, $keyOne, $modelTwo, $keyTwo): void
63
    {
64
        $relations = [$modelOne => $keyOne, $modelTwo => $keyTwo];
65
        self::setRelations($modelOne, $modelTwo, $relations);
66
    }
67
68
    /**
69
     * Setup multi relations
70
     *
71
     * @param  string $modelOne
72
     * @param  string $modelTwo
73
     * @param  array  $relations
74
     *
75
     * @return void
76
     */
77
    public static function setRelations($modelOne, $modelTwo, $relations): void
78
    {
79
        $name = [$modelOne, $modelTwo];
80
        sort($name);
81
        $name = implode(':', $name);
82
        // create record in static variable
83
        self::$relations[$name] = $relations;
84
    }
85
86
    /**
87
     * Get relations
88
     *
89
     * @param  string $modelOne
90
     * @param  string $modelTwo
91
     *
92
     * @return array|false
93
     */
94
    public static function getRelations($modelOne, $modelTwo)
95
    {
96
        $name = [$modelOne, $modelTwo];
97
        sort($name);
98
        $name = implode(':', $name);
99
100
        return self::$relations[$name] ?? false;
101
    }
102
103
    /**
104
     * findRelation
105
     *
106
     * @param  Row    $row
107
     * @param  string $relation
108
     *
109
     * @return array
110
     * @throws Exception\TableNotFoundException
111
     * @throws Exception\RelationNotFoundException
112
     */
113 1
    public static function findRelation($row, $relation): array
114
    {
115 1
        $model = $row->getTable()->getModel();
116
117
        /** @var \Bluz\Db\Table $relationTable */
118 1
        $relationTable = self::getModelClass($relation);
119
        $relationTable::getInstance();
120
121
        if (!$relations = self::getRelations($model, $relation)) {
122
            throw new RelationNotFoundException(
123
                "Relations between model `$model` and `$relation` is not defined"
124
            );
125
        }
126
127
        // check many-to-many relations
128
        if (\count($relations) === 1) {
129
            $relations = self::getRelations($model, current($relations));
130
        }
131
132
        $field = $relations[$model];
133
        $key = $row->{$field};
134
135
        return self::findRelations($model, $relation, [$key]);
136
    }
137
138
    /**
139
     * Find Relations between two tables
140
     *
141
     * @param  string $modelOne Table
142
     * @param  string $modelTwo Target table
143
     * @param  array  $keys     Keys from first table
144
     *
145
     * @return array
146
     * @throws Exception\RelationNotFoundException
147
     */
148
    public static function findRelations($modelOne, $modelTwo, $keys): array
149
    {
150
        $keys = (array)$keys;
151
        if (!$relations = self::getRelations($modelOne, $modelTwo)) {
152
            throw new RelationNotFoundException("Relations between model `$modelOne` and `$modelTwo` is not defined");
153
        }
154
155
        /* @var Table $tableOneClass name */
156
        $tableOneClass = self::getModelClass($modelOne);
157
158
        /* @var string $tableOneName */
159
        $tableOneName = $tableOneClass::getInstance()->getName();
160
161
        /* @var Table $tableTwoClass name */
162
        $tableTwoClass = self::getModelClass($modelTwo);
163
164
        /* @var string $tableTwoName */
165
        $tableTwoName = $tableTwoClass::getInstance()->getName();
166
167
        /* @var Query\Select $tableTwoSelect */
168
        $tableTwoSelect = $tableTwoClass::getInstance()::select();
169
170
        // check many to many relation
171
        if (\is_int(\array_keys($relations)[0])) {
172
            // many to many relation over third table
173
            $modelThree = $relations[0];
174
175
            // relations between target table and third table
176
            $relations = self::getRelations($modelTwo, $modelThree);
177
178
            /* @var Table $tableThreeClass name */
179
            $tableThreeClass = self::getModelClass($modelThree);
180
181
            /* @var string $tableTwoName */
182
            $tableThreeName = $tableThreeClass::getInstance()->getName();
183
184
            // join it to query
185
            $tableTwoSelect->join(
186
                $tableTwoName,
187
                $tableThreeName,
188
                $tableThreeName,
189
                $tableTwoName . '.' . $relations[$modelTwo] . '=' . $tableThreeName . '.' . $relations[$modelThree]
190
            );
191
192
            // relations between source table and third table
193
            $relations = self::getRelations($modelOne, $modelThree);
194
195
            // join it to query
196
            $tableTwoSelect->join(
197
                $tableThreeName,
198
                $tableOneName,
199
                $tableOneName,
200
                $tableThreeName . '.' . $relations[$modelThree] . '=' . $tableOneName . '.' . $relations[$modelOne]
201
            );
202
203
            // set source keys
204
            $tableTwoSelect->where($tableOneName . '.' . $relations[$modelOne] . ' IN (?)', $keys);
0 ignored issues
show
Bug introduced by
$keys of type array is incompatible with the type string expected by parameter $conditions of Bluz\Db\Query\Select::where(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

204
            $tableTwoSelect->where($tableOneName . '.' . $relations[$modelOne] . ' IN (?)', /** @scrutinizer ignore-type */ $keys);
Loading history...
205
        } else {
206
            // set source keys
207
            $tableTwoSelect->where($relations[$modelTwo] . ' IN (?)', $keys);
208
        }
209
        return $tableTwoSelect->execute();
210
    }
211
212
    /**
213
     * Add information about model's classes
214
     *
215
     * @param  string $model
216
     * @param  string $className
217
     *
218
     * @return void
219
     */
220 2
    public static function addClassMap($model, $className): void
221
    {
222 2
        self::$modelClassMap[$model] = $className;
223 2
    }
224
225
    /**
226
     * Get information about Model classes
227
     *
228
     * @param  string $model
229
     *
230
     * @return string
231
     * @throws Exception\RelationNotFoundException
232
     */
233 1
    public static function getModelClass($model): string
234
    {
235 1
        if (!isset(self::$modelClassMap[$model])) {
236
            // try to detect
237 1
            $className = '\\Application\\' . $model . '\\Table';
238
239 1
            if (!class_exists($className)) {
240 1
                throw new RelationNotFoundException("Related class for model `$model` not found");
241
            }
242
            self::$modelClassMap[$model] = $className;
243
        }
244
        return self::$modelClassMap[$model];
245
    }
246
247
    /**
248
     * Get information about Table classes
249
     *
250
     * @param  string $modelName
251
     * @param  array  $data
252
     *
253
     * @return RowInterface
254
     * @throws Exception\RelationNotFoundException
255
     */
256
    public static function createRow($modelName, $data): RowInterface
257
    {
258
        $tableClass = self::getModelClass($modelName);
259
260
        /* @var Table $tableClass name */
261
        return $tableClass::getInstance()::create($data);
262
    }
263
264
    /**
265
     * Fetch by Divider
266
     *
267
     * @param  array $input
268
     *
269
     * @return array
270
     * @throws Exception\RelationNotFoundException
271
     */
272
    public static function fetch($input): array
273
    {
274
        $output = [];
275
        $map = [];
276
        foreach ($input as $i => $row) {
277
            $model = '';
278
            foreach ($row as $key => $value) {
279
                if (strpos($key, '__') === 0) {
280
                    $model = substr($key, 2);
281
                    continue;
282
                }
283
                $map[$i][$model][$key] = $value;
284
            }
285
            foreach ($map[$i] as $model => &$data) {
286
                $data = self::createRow($model, $data);
287
            }
288
            $output[] = $map;
289
        }
290
        return $output;
291
    }
292
}
293