Completed
Push — master ( e91368...26d5da )
by Anton
9s
created

Relations::getModelClass()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.2098

Importance

Changes 0
Metric Value
cc 3
eloc 7
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 13
rs 9.4285
ccs 5
cts 7
cp 0.7143
crap 3.2098
1
<?php
2
/**
3
 * Bluz Framework Component
4
 *
5
 * @copyright Bluz PHP Team
6
 * @link https://github.com/bluzphp/framework
7
 */
8
9
/**
10
 * @namespace
11
 */
12
namespace Bluz\Db;
13
14
use Bluz\Db\Exception\RelationNotFoundException;
15
16
/**
17
 * Relations map of Db tables
18
 *
19
 * @package  Bluz\Db
20
 * @author   Anton Shevchuk
21
 */
22
class Relations
23
{
24
    /**
25
     * Relation stack, i.e.
26
     * <code>
27
     *     [
28
     *         'Model1:Model2' => ['Model1'=>'foreignKey', 'Model2'=>'primaryKey'],
29
     *         'Pages:Users' => ['Pages'=>'userId', 'Users'=>'id'],
30
     *         'PagesTags:Pages' => ['PagesTags'=>'pageId', 'Pages'=>'id'],
31
     *         'PagesTags:Tags' => ['PagesTags'=>'tagId', 'Tags'=>'id'],
32
     *         'Pages:Tags' => ['PagesTags'],
33
     *     ]
34
     * </code>
35
     *
36
     * @var array
37
     */
38
    protected static $relations;
39
40
    /**
41
     * Class map, i.e.
42
     * <code>
43
     *     [
44
     *         'Pages' => '\Application\Pages\Table',
45
     *         'Users' => '\Application\Users\Table',
46
     *     ]
47
     * </code>
48
     *
49
     * @var array
50
     */
51
    protected static $modelClassMap;
52
53
    /**
54
     * Setup relation between two models
55
     *
56
     * @param  string $modelOne
57
     * @param  string $keyOne
58
     * @param  string $modelTwo
59
     * @param  string $keyTwo
60
     * @return void
61
     */
62
    public static function setRelation($modelOne, $keyOne, $modelTwo, $keyTwo)
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
     * @return void
75
     */
76
    public static function setRelations($modelOne, $modelTwo, $relations)
77
    {
78
        $name = [$modelOne, $modelTwo];
79
        sort($name);
80
        $name = join(':', $name);
81
        // create record in static variable
82
        self::$relations[$name] = $relations;
83
    }
84
85
    /**
86
     * Get relations
87
     *
88
     * @param  string $modelOne
89
     * @param  string $modelTwo
90
     * @return array|false
91
     */
92
    public static function getRelations($modelOne, $modelTwo)
93
    {
94
        $name = [$modelOne, $modelTwo];
95
        sort($name);
96
        $name = join(':', $name);
97
98
        return self::$relations[$name] ?? false;
99
    }
100
101
    /**
102
     * findRelation
103
     *
104
     * @param  Row $row
105
     * @param  string $relation
106
     * @return array
107
     * @throws Exception\RelationNotFoundException
108
     */
109 1
    public static function findRelation($row, $relation)
110
    {
111 1
        $model = $row->getTable()->getModel();
112
113
        /** @var \Bluz\Db\Table $relationTable */
114 1
        $relationTable = Relations::getModelClass($relation);
115
        $relationTable::getInstance();
116
117
        if (!$relations = Relations::getRelations($model, $relation)) {
118
            throw new RelationNotFoundException(
119
                "Relations between model `$model` and `$relation` is not defined"
120
            );
121
        }
122
123
        // check many-to-many relations
124
        if (sizeof($relations) == 1) {
125
            $relations = Relations::getRelations($model, current($relations));
126
        }
127
128
        $field = $relations[$model];
129
        $key = $row->{$field};
130
131
        return Relations::findRelations($model, $relation, [$key]);
132
    }
133
134
    /**
135
     * Find Relations between two tables
136
     *
137
     * @param  string $modelOne Table
138
     * @param  string $modelTwo Target table
139
     * @param  array  $keys     Keys from first table
140
     * @return array
141
     * @throws Exception\RelationNotFoundException
142
     */
143
    public static function findRelations($modelOne, $modelTwo, $keys)
144
    {
145
        $keys = (array) $keys;
146
        if (!$relations = self::getRelations($modelOne, $modelTwo)) {
147
            throw new RelationNotFoundException("Relations between model `$modelOne` and `$modelTwo` is not defined");
148
        }
149
150
        /* @var Table $tableOneClass name */
151
        $tableOneClass = self::getModelClass($modelOne);
152
153
        /* @var string $tableOneName */
154
        $tableOneName = $tableOneClass::getInstance()->getName();
155
156
        /* @var Table $tableTwoClass name */
157
        $tableTwoClass = self::getModelClass($modelTwo);
158
159
        /* @var string $tableTwoName */
160
        $tableTwoName = $tableTwoClass::getInstance()->getName();
161
162
        /* @var Query\Select $tableTwoSelect */
163
        $tableTwoSelect = $tableTwoClass::getInstance()->select();
164
165
        // check many to many relation
166
        if (is_int(array_keys($relations)[0])) {
167
            // many to many relation over third table
168
            $modelThree = $relations[0];
169
170
            // relations between target table and third table
171
            $relations = self::getRelations($modelTwo, $modelThree);
172
173
            /* @var Table $tableThreeClass name */
174
            $tableThreeClass = self::getModelClass($modelThree);
175
176
            /* @var string $tableTwoName */
177
            $tableThreeName = $tableThreeClass::getInstance()->getName();
178
179
            // join it to query
180
            $tableTwoSelect->join(
181
                $tableTwoName,
182
                $tableThreeName,
183
                $tableThreeName,
184
                $tableTwoName.'.'.$relations[$modelTwo].'='.$tableThreeName.'.'.$relations[$modelThree]
185
            );
186
187
            // relations between source table and third table
188
            $relations = self::getRelations($modelOne, $modelThree);
189
190
            // join it to query
191
            $tableTwoSelect->join(
192
                $tableThreeName,
193
                $tableOneName,
194
                $tableOneName,
195
                $tableThreeName.'.'.$relations[$modelThree].'='.$tableOneName.'.'.$relations[$modelOne]
196
            );
197
198
            // set source keys
199
            $tableTwoSelect->where($tableOneName.'.'. $relations[$modelOne] .' IN (?)', $keys);
200
        } else {
201
            // set source keys
202
            $tableTwoSelect->where($relations[$modelTwo] .' IN (?)', $keys);
203
        }
204
        return $tableTwoSelect->execute();
205
    }
206
207
    /**
208
     * Add information about model's classes
209
     *
210
     * @param  string $model
211
     * @param  string $className
212
     * @return void
213
     */
214 1
    public static function addClassMap($model, $className)
215
    {
216 1
        self::$modelClassMap[$model] = $className;
217 1
    }
218
219
    /**
220
     * Get information about Model classes
221
     *
222
     * @param  string $model
223
     * @return string
224
     * @throws Exception\RelationNotFoundException
225
     */
226 1
    public static function getModelClass($model)
227
    {
228 1
        if (!isset(self::$modelClassMap[$model])) {
229
            // try to detect
230 1
            $className = '\\Application\\'.$model.'\\Table';
231
232 1
            if (!class_exists($className)) {
233 1
                throw new RelationNotFoundException("Related class for model `$model` not found");
234
            }
235
            self::$modelClassMap[$model] = $className;
236
        }
237
        return self::$modelClassMap[$model];
238
    }
239
240
    /**
241
     * Get information about Table classes
242
     *
243
     * @param  string $modelName
244
     * @param  array  $data
245
     * @return Row
246
     * @throws Exception\RelationNotFoundException
247
     */
248
    public static function createRow($modelName, $data)
249
    {
250
        $tableClass = self::getModelClass($modelName);
251
252
        /* @var Table $tableClass name */
253
        return $tableClass::getInstance()->create($data);
254
    }
255
256
    /**
257
     * Fetch by Divider
258
     *
259
     * @param  array $input
260
     * @return array
261
     */
262
    public static function fetch($input)
263
    {
264
        $output = [];
265
        $map = [];
266
        foreach ($input as $i => $row) {
267
            $model = '';
268
            foreach ($row as $key => $value) {
269
                if (strpos($key, '__') === 0) {
270
                    $model = substr($key, 2);
271
                    continue;
272
                }
273
                $map[$i][$model][$key] = $value;
274
            }
275
            foreach ($map[$i] as $model => &$data) {
276
                $data = self::createRow($model, $data);
277
            }
278
            $output[] = $map;
279
        }
280
        return $output;
281
    }
282
}
283