Passed
Push — develop ( 33032a...666b96 )
by Henry
02:03
created

Relations::_initRelationship()   F

Complexity

Conditions 36
Paths 1378

Size

Total Lines 121
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 36
eloc 65
nc 1378
nop 2
dl 0
loc 121
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
 * This file is part of the Divergence package.
4
 *
5
 * (c) Henry Paradiz <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Divergence\Models;
12
13
use Exception;
14
15
use ReflectionClass;
16
17
use Divergence\IO\Database\MySQL as DB;
18
19
/**
20
 * Relations.
21
 *
22
 * @package Divergence
23
 * @author  Henry Paradiz <[email protected]>
24
 *
25
 * @property array $_classRelationships
26
 * @property array $_classFields
27
 * @property string $rootClass
28
 * @property array $contextClasses
29
 */
30
trait Relations
31
{
32
    protected $_relatedObjects = [];
33
34
    public static function _relationshipExists($relationship)
35
    {
36
        if (is_array(static::$_classRelationships[get_called_class()])) {
37
            return array_key_exists($relationship, static::$_classRelationships[get_called_class()]);
38
        } else {
39
            return false;
40
        }
41
    }
42
43
    /**
44
     * Called when anything relationships related is used for the first time to define relationships before _initRelationships
45
     */
46
    protected static function _defineRelationships()
47
    {
48
        $className = get_called_class();
49
        $classes = class_parents($className);
50
        array_unshift($classes, $className);
51
        static::$_classRelationships[$className] = [];
52
        while ($class = array_pop($classes)) {
53
            if (!empty($class::$relationships)) {
54
                static::$_classRelationships[$className] = array_merge(static::$_classRelationships[$className], $class::$relationships);
55
            }
56
            if (static::isVersioned() && !empty($class::$versioningRelationships)) {
57
                static::$_classRelationships[$className] = array_merge(static::$_classRelationships[$className], $class::$versioningRelationships);
58
            }
59
        }
60
    }
61
62
63
    /**
64
     * Called after _defineRelationships to initialize and apply defaults to the relationships property
65
     * Must be idempotent as it may be applied multiple times up the inheritence chain
66
     */
67
    protected static function _initRelationships()
68
    {
69
        $className = get_called_class();
70
        if (!empty(static::$_classRelationships[$className])) {
71
            $relationships = [];
72
            foreach (static::$_classRelationships[$className] as $relationship => $options) {
73
                if (is_array($options)) {
74
                    $relationships[$relationship] = static::_initRelationship($relationship, $options);
75
                }
76
            }
77
            static::$_classRelationships[$className] = $relationships;
78
        }
79
    }
80
81
    // TODO: Make relations getPrimaryKeyValue() instead of using ID all the time.
82
    protected static function _initRelationship($relationship, $options)
83
    {
84
        $classShortName = basename(str_replace('\\', '/', static::$rootClass));
85
86
        // apply defaults
87
        if (empty($options['type'])) {
88
            $options['type'] = 'one-one';
89
        }
90
91
        if ($options['type'] == 'one-one') {
92
            if (empty($options['local'])) {
93
                $options['local'] = $relationship . 'ID';
94
            }
95
96
            if (empty($options['foreign'])) {
97
                $options['foreign'] = 'ID';
98
            }
99
        } elseif ($options['type'] == 'one-many') {
100
            if (empty($options['local'])) {
101
                $options['local'] = 'ID';
102
            }
103
104
            if (empty($options['foreign'])) {
105
                $options['foreign'] =  $classShortName. 'ID';
106
            }
107
108
            if (!isset($options['indexField'])) {
109
                $options['indexField'] = false;
110
            }
111
112
            if (!isset($options['conditions'])) {
113
                $options['conditions'] = [];
114
            } elseif (is_string($options['conditions'])) {
115
                $options['conditions'] = [$options['conditions']];
116
            }
117
118
            if (!isset($options['order'])) {
119
                $options['order'] = false;
120
            }
121
        } elseif ($options['type'] == 'context-children') {
122
            if (empty($options['local'])) {
123
                $options['local'] = 'ID';
124
            }
125
126
            if (empty($options['contextClass'])) {
127
                $options['contextClass'] = get_called_class();
128
            }
129
130
            if (!isset($options['indexField'])) {
131
                $options['indexField'] = false;
132
            }
133
134
            if (!isset($options['conditions'])) {
135
                $options['conditions'] = [];
136
            }
137
138
            if (!isset($options['order'])) {
139
                $options['order'] = false;
140
            }
141
        } elseif ($options['type'] == 'context-parent') {
142
            if (empty($options['local'])) {
143
                $options['local'] = 'ContextID';
144
            }
145
146
            if (empty($options['foreign'])) {
147
                $options['foreign'] = 'ID';
148
            }
149
150
            if (empty($options['classField'])) {
151
                $options['classField'] = 'ContextClass';
152
            }
153
154
            if (empty($options['allowedClasses'])) {
155
                $options['allowedClasses'] = static::$contextClasses;
156
            }
157
        } elseif ($options['type'] == 'many-many') {
158
            if (empty($options['class'])) {
159
                throw new Exception('Relationship type many-many option requires a class setting.');
160
            }
161
162
            if (empty($options['linkClass'])) {
163
                throw new Exception('Relationship type many-many option requires a linkClass setting.');
164
            }
165
166
            if (empty($options['linkLocal'])) {
167
                $options['linkLocal'] = $classShortName . 'ID';
168
            }
169
170
            if (empty($options['linkForeign'])) {
171
                $foreignShortname = basename(str_replace('\\', '/', $options['class']::$rootClass));
172
                $options['linkForeign'] =  $foreignShortname . 'ID';
173
            }
174
175
            if (empty($options['local'])) {
176
                $options['local'] = 'ID';
177
            }
178
179
            if (empty($options['foreign'])) {
180
                $options['foreign'] = 'ID';
181
            }
182
183
            if (!isset($options['indexField'])) {
184
                $options['indexField'] = false;
185
            }
186
187
            if (!isset($options['conditions'])) {
188
                $options['conditions'] = [];
189
            }
190
191
            if (!isset($options['order'])) {
192
                $options['order'] = false;
193
            }
194
        }
195
196
        if (static::isVersioned() && $options['type'] == 'history') {
197
            if (empty($options['class'])) {
198
                $options['class'] = get_called_class();
199
            }
200
        }
201
202
        return $options;
203
    }
204
205
    /**
206
     * Retrieves given relationships' value
207
     * @param string $relationship Name of relationship
208
     * @return mixed value
209
     */
210
    protected function _getRelationshipValue($relationship)
211
    {
212
        if (!isset($this->_relatedObjects[$relationship])) {
213
            $rel = static::$_classRelationships[get_called_class()][$relationship];
214
215
            if ($rel['type'] == 'one-one') {
216
                if ($value = $this->_getFieldValue($rel['local'])) {
217
                    $this->_relatedObjects[$relationship] = $rel['class']::getByField($rel['foreign'], $value);
218
219
                    // hook relationship for invalidation
220
                    static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
221
                } else {
222
                    $this->_relatedObjects[$relationship] = null;
223
                }
224
            } elseif ($rel['type'] == 'one-many') {
225
                if (!empty($rel['indexField']) && !$rel['class']::fieldExists($rel['indexField'])) {
226
                    $rel['indexField'] = false;
227
                }
228
229
                $this->_relatedObjects[$relationship] = $rel['class']::getAllByWhere(
230
                    array_merge($rel['conditions'], [
231
                        $rel['foreign'] => $this->_getFieldValue($rel['local']),
232
                    ]),
233
                    [
234
                        'indexField' => $rel['indexField'],
235
                        'order' => $rel['order'],
236
                        'conditions' => $rel['conditions'],
237
                    ]
238
                );
239
240
241
                // hook relationship for invalidation
242
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
243
            } elseif ($rel['type'] == 'context-children') {
244
                if (!empty($rel['indexField']) && !$rel['class']::fieldExists($rel['indexField'])) {
245
                    $rel['indexField'] = false;
246
                }
247
248
                $conditions = array_merge($rel['conditions'], [
249
                    'ContextClass' => $rel['contextClass'],
250
                    'ContextID' => $this->_getFieldValue($rel['local']),
251
                ]);
252
253
                $this->_relatedObjects[$relationship] = $rel['class']::getAllByWhere(
254
                    $conditions,
255
                    [
256
                        'indexField' => $rel['indexField'],
257
                        'order' => $rel['order'],
258
                    ]
259
                );
260
261
                // hook relationship for invalidation
262
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
263
            } elseif ($rel['type'] == 'context-parent') {
264
                $className = $this->_getFieldValue($rel['classField']);
265
                $this->_relatedObjects[$relationship] = $className ? $className::getByID($this->_getFieldValue($rel['local'])) : null;
266
267
                // hook both relationships for invalidation
268
                static::$_classFields[get_called_class()][$rel['classField']]['relationships'][$relationship] = true;
269
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
270
            } elseif ($rel['type'] == 'many-many') {
271
                if (!empty($rel['indexField']) && !$rel['class']::fieldExists($rel['indexField'])) {
272
                    $rel['indexField'] = false;
273
                }
274
275
                // TODO: support indexField, conditions, and order
276
277
                $this->_relatedObjects[$relationship] = $rel['class']::getAllByQuery(
278
                    'SELECT Related.* FROM `%s` Link JOIN `%s` Related ON (Related.`%s` = Link.%s) WHERE Link.`%s` = %u AND %s',
279
                    [
280
                        $rel['linkClass']::$tableName,
281
                        $rel['class']::$tableName,
282
                        $rel['foreign'],
283
                        $rel['linkForeign'],
284
                        $rel['linkLocal'],
285
                        $this->_getFieldValue($rel['local']),
286
                        $rel['conditions'] ? join(' AND ', $rel['conditions']) : '1',
287
                    ]
288
                );
289
290
                // hook relationship for invalidation
291
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
292
            } elseif ($rel['type'] == 'history' && static::isVersioned()) {
293
                $this->_relatedObjects[$relationship] = $rel['class']::getRevisionsByID($this->getPrimaryKeyValue(), $rel);
294
            }
295
        }
296
297
        return $this->_relatedObjects[$relationship];
298
    }
299
}
300