Relations::_getRelationshipValue()   D
last analyzed

Complexity

Conditions 18
Paths 13

Size

Total Lines 88
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 44
CRAP Score 25.0138

Importance

Changes 0
Metric Value
cc 18
eloc 50
c 0
b 0
f 0
nc 13
nop 1
dl 0
loc 88
ccs 44
cts 61
cp 0.7213
crap 25.0138
rs 4.8666

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