Passed
Branch release (bd7374)
by Henry
03:48
created

Relations::_getRelationshipValue()   D

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 15
    public static function _relationshipExists($relationship)
31
    {
32 15
        if (is_array(static::$_classRelationships[get_called_class()])) {
33 15
            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 2
        $options['local'] = $options['local'] ?? 'ID';
97 2
        $options['contextClass'] = $options['contextClass'] ?? get_called_class();
98 2
        $options['indexField'] = $options['indexField'] ?? false;
99 2
        $options['conditions'] = $options['conditions'] ?? [];
100 2
        $options['order'] = $options['order'] ?? false;
101 2
        return $options;
102
    }
103
104 2
    protected static function _prepareContextParent($options): array {
105 2
        $options['local'] = $options['local'] ?? 'ContextID';
106 2
        $options['foreign'] = $options['foreign'] ?? 'ID';
107 2
        $options['classField'] = $options['classField'] ?? 'ContextClass';
108 2
        $options['allowedClasses'] = $options['allowedClasses'] ?? (!empty(static::$contextClasses)?static::$contextClasses:null);
109 2
        return $options;
110
    }
111
112 3
    protected static function _prepareManyMany($classShortName, $options): array {
113 3
        if (empty($options['class'])) {
114 1
            throw new Exception('Relationship type many-many option requires a class setting.');
115
        }
116
117 2
        if (empty($options['linkClass'])) {
118 1
            throw new Exception('Relationship type many-many option requires a linkClass setting.');
119
        }
120
121 1
        $options['linkLocal'] = $options['linkLocal'] ?? $classShortName . 'ID';
122 1
        $options['linkForeign'] = $options['linkForeign'] ?? basename(str_replace('\\', '/', $options['class']::$rootClass)).'ID';
123 1
        $options['local'] = $options['local'] ?? 'ID';
124 1
        $options['foreign'] = $options['foreign'] ?? 'ID';
125 1
        $options['indexField'] = $options['indexField'] ?? false;
126 1
        $options['conditions'] = $options['conditions'] ?? [];
127 1
        $options['order'] = $options['order'] ?? false;
128 1
        return $options;
129
    }
130
131
    // TODO: Make relations getPrimaryKeyValue() instead of using ID all the time.
132 8
    protected static function _initRelationship($relationship, $options)
133
    {
134 8
        $classShortName = basename(str_replace('\\', '/', static::$rootClass));
135
136
        // apply defaults
137 8
        if (empty($options['type'])) {
138 3
            $options['type'] = 'one-one';
139
        }
140
141 8
        switch($options['type']) {
142 8
            case 'one-one':
143 3
                $options = static::_prepareOneOne($relationship, $options);
144 3
                break;
145 7
            case 'one-many':
146 3
                $options = static::_prepareOneMany($classShortName, $options);
147 3
                break;
148 7
            case 'context-children':
149 2
                $options = static::_prepareContextChildren($options);
150 2
                break;
151 6
            case 'context-parent':
152 2
                $options = static::_prepareContextParent($options);
153 2
                break;
154 5
            case 'many-many':
155 3
                $options = static::_prepareManyMany($classShortName,$options);
156 1
                break;
157
        }
158
159 6
        if (static::isVersioned() && $options['type'] == 'history') {
160 2
            if (empty($options['class'])) {
161 2
                $options['class'] = get_called_class();
162
            }
163
        }
164
165 6
        return $options;
166
    }
167
168
    /**
169
     * Retrieves given relationship's value
170
     * @param string $relationship Name of relationship
171
     * @return mixed value
172
     */
173 10
    protected function _getRelationshipValue($relationship)
174
    {
175 10
        if (!isset($this->_relatedObjects[$relationship])) {
176 10
            $rel = static::$_classRelationships[get_called_class()][$relationship];
177
178 10
            if ($rel['type'] == 'one-one') {
179 2
                if ($value = $this->_getFieldValue($rel['local'])) {
180 2
                    $this->_relatedObjects[$relationship] = $rel['class']::getByField($rel['foreign'], $value);
181
182
                    // hook relationship for invalidation
183 2
                    static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
184
                } else {
185 2
                    $this->_relatedObjects[$relationship] = null;
186
                }
187 8
            } elseif ($rel['type'] == 'one-many') {
188 3
                if (!empty($rel['indexField']) && !$rel['class']::fieldExists($rel['indexField'])) {
189
                    $rel['indexField'] = false;
190
                }
191
192 3
                $this->_relatedObjects[$relationship] = $rel['class']::getAllByWhere(
193 3
                    array_merge($rel['conditions'], [
194 3
                        $rel['foreign'] => $this->_getFieldValue($rel['local']),
195 3
                    ]),
196 3
                    [
197 3
                        'indexField' => $rel['indexField'],
198 3
                        'order' => $rel['order'],
199 3
                        'conditions' => $rel['conditions'],
200 3
                    ]
201 3
                );
202
203
204
                // hook relationship for invalidation
205 3
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
206 5
            } elseif ($rel['type'] == 'context-children') {
207 1
                if (!empty($rel['indexField']) && !$rel['class']::fieldExists($rel['indexField'])) {
208
                    $rel['indexField'] = false;
209
                }
210
211 1
                $conditions = array_merge($rel['conditions'], [
212 1
                    'ContextClass' => $rel['contextClass'],
213 1
                    'ContextID' => $this->_getFieldValue($rel['local']),
214 1
                ]);
215
216 1
                $this->_relatedObjects[$relationship] = $rel['class']::getAllByWhere(
217 1
                    $conditions,
218 1
                    [
219 1
                        'indexField' => $rel['indexField'],
220 1
                        'order' => $rel['order'],
221 1
                    ]
222 1
                );
223
224
                // hook relationship for invalidation
225 1
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
226 4
            } elseif ($rel['type'] == 'context-parent') {
227 1
                $className = $this->_getFieldValue($rel['classField']);
228 1
                $this->_relatedObjects[$relationship] = $className ? $className::getByID($this->_getFieldValue($rel['local'])) : null;
229
230
                // hook both relationships for invalidation
231 1
                static::$_classFields[get_called_class()][$rel['classField']]['relationships'][$relationship] = true;
232 1
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
233 3
            } elseif ($rel['type'] == 'many-many') {
234
                if (!empty($rel['indexField']) && !$rel['class']::fieldExists($rel['indexField'])) {
235
                    $rel['indexField'] = false;
236
                }
237
238
                // TODO: support indexField, conditions, and order
239
240
                $this->_relatedObjects[$relationship] = $rel['class']::getAllByQuery(
241
                    'SELECT Related.* FROM `%s` Link JOIN `%s` Related ON (Related.`%s` = Link.%s) WHERE Link.`%s` = %u AND %s',
242
                    [
243
                        $rel['linkClass']::$tableName,
244
                        $rel['class']::$tableName,
245
                        $rel['foreign'],
246
                        $rel['linkForeign'],
247
                        $rel['linkLocal'],
248
                        $this->_getFieldValue($rel['local']),
249
                        $rel['conditions'] ? join(' AND ', $rel['conditions']) : '1',
250
                    ]
251
                );
252
253
                // hook relationship for invalidation
254
                static::$_classFields[get_called_class()][$rel['local']]['relationships'][$relationship] = true;
255 3
            } elseif ($rel['type'] == 'history' && static::isVersioned()) {
256 2
                $this->_relatedObjects[$relationship] = $rel['class']::getRevisionsByID($this->getPrimaryKeyValue(), $rel);
257
            }
258
        }
259
260 10
        return $this->_relatedObjects[$relationship];
261
    }
262
}
263