Test Setup Failed
Pull Request — master (#47)
by Alex
03:02
created

MetadataProvider   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 306
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 7.14%

Importance

Changes 11
Bugs 0 Features 0
Metric Value
wmc 39
c 11
b 0
f 0
lcom 1
cbo 6
dl 0
loc 306
ccs 5
cts 70
cp 0.0714
rs 8.2857

9 Methods

Rating   Name   Duplication   Size   Complexity  
B boot() 0 38 6
A setupRoute() 0 8 1
A register() 0 6 1
B getCandidateModels() 0 14 5
B getEntityTypesAndResourceSets() 0 28 3
B calculateRoundTripRelations() 0 33 4
C calculateRoundTripRelationsSecondPass() 0 49 7
C calculateRoundTripRelationsFirstPass() 0 53 11
B calculateRoundTripRelationsGenForwardReverse() 0 26 1
1
<?php
2
3
namespace AlgoWeb\PODataLaravel\Providers;
4
5
use Illuminate\Support\Facades\App;
6
use Illuminate\Support\ServiceProvider;
7
use Illuminate\Support\Facades\Cache;
8
use POData\Providers\Metadata\IMetadataProvider;
9
use POData\Providers\Metadata\SimpleMetadataProvider;
10
use Illuminate\Support\Facades\Route;
11
use Illuminate\Support\Facades\Schema as Schema;
12
13
class MetadataProvider extends MetadataBaseProvider
14
{
15
    protected $multConstraints = [ '0..1' => ['1'], '1' => ['0..1', '*'], '*' => ['1', '*']];
16
    protected static $METANAMESPACE = "Data";
17
18
    /**
19
     * Bootstrap the application services.  Post-boot.
20 1
     *
21
     * @return void
22 1
     */
23
    public function boot()
24
    {
25 1
        self::$METANAMESPACE = env('ODataMetaNamespace', 'Data');
26
        // If we aren't migrated, there's no DB tables to pull metadata _from_, so bail out early
27
        try {
28 1
            if (!Schema::hasTable('migrations')) {
29 1
                return;
30
            }
31
        } catch (\Exception $e) {
32
            return;
33
        }
34
35
        self::setupRoute();
36
        $isCaching = true === $this->getIsCaching();
37
        $hasCache = Cache::has('metadata');
38
39
        if ($isCaching && $hasCache) {
40
            $meta = Cache::get('metadata');
41
            App::instance('metadata', $meta);
1 ignored issue
show
Bug introduced by
The method instance() does not exist on Illuminate\Support\Facades\App. Did you maybe mean clearResolvedInstance()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
42
            return;
43
        }
44
        $meta = App::make('metadata');
45
46
        $modelNames = $this->getCandidateModels();
47
48
        list($EntityTypes, $ResourceSets, $ends) = $this->getEntityTypesAndResourceSets($meta, $modelNames);
49
50
        // now that endpoints are hooked up, tackle the relationships
51
        // if we'd tried earlier, we'd be guaranteed to try to hook a relation up to null, which would be bad
52
        foreach ($ends as $bitter) {
53
            $fqModelName = $bitter;
54
            $instance = new $fqModelName();
55
            $instance->hookUpRelationships($EntityTypes, $ResourceSets);
56
        }
57
58
        $key = 'metadata';
59
        $this->handlePostBoot($isCaching, $hasCache, $key, $meta);
60
    }
61
62
    private static function setupRoute()
63
    {
64
        $valueArray = [];
0 ignored issues
show
Unused Code introduced by
$valueArray is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
65
66
        Route::any('odata.svc/{section}', 'AlgoWeb\PODataLaravel\Controllers\ODataController@index')
67
            ->where(['section' => '.*']);
68
        Route::any('odata.svc', 'AlgoWeb\PODataLaravel\Controllers\ODataController@index');
69
    }
70
71
    /**
72
     * Register the application services.  Boot-time only.
73
     *
74
     * @return void
75
     */
76
    public function register()
77
    {
78
        $this->app->singleton('metadata', function ($app) {
0 ignored issues
show
Unused Code introduced by
The parameter $app is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
79
            return new SimpleMetadataProvider('Data', self::$METANAMESPACE);
80
        });
81
    }
82
83
    /**
84
     * @return array
85
     */
86
    protected function getCandidateModels()
87
    {
88
        $Classes = $this->getClassMap();
89
        $ends = [];
90
        $startName = defined('PODATA_LARAVEL_APP_ROOT_NAMESPACE') ? PODATA_LARAVEL_APP_ROOT_NAMESPACE : "App";
91
        foreach ($Classes as $name) {
92
            if (\Illuminate\Support\Str::startsWith($name, $startName)) {
93
                if (in_array("AlgoWeb\\PODataLaravel\\Models\\MetadataTrait", class_uses($name))) {
94
                    $ends[] = $name;
95
                }
96
            }
97
        }
98
        return $ends;
99
    }
100
101
    /**
102
     * @param $meta
103
     * @param $ends
104
     * @return array
105
     */
106
    protected function getEntityTypesAndResourceSets($meta, $ends)
107
    {
108
        assert($meta instanceof IMetadataProvider, get_class($meta));
109
        $EntityTypes = [];
110
        $ResourceSets = [];
111
        $begins = [];
112
        $numEnds = count($ends);
113
114
        for ($i = 0; $i < $numEnds; $i++) {
115
            $bitter = $ends[$i];
116
            $fqModelName = $bitter;
117
118
            $instance = App::make($fqModelName);
119
            $name = strtolower($instance->getEndpointName());
120
            $metaSchema = $instance->getXmlSchema();
121
122
            // if for whatever reason we don't get an XML schema, move on to next entry and drop current one from
123
            // further processing
124
            if (null == $metaSchema) {
125
                continue;
126
            }
127
            $EntityTypes[$fqModelName] = $metaSchema;
128
            $ResourceSets[$fqModelName] = $meta->addResourceSet($name, $metaSchema);
129
            $begins[] = $bitter;
130
        }
131
132
        return array($EntityTypes, $ResourceSets, $begins);
133
    }
134
135
    public function calculateRoundTripRelations()
136
    {
137
        $modelNames = $this->getCandidateModels();
138
139
        $hooks = [];
140
        foreach ($modelNames as $name) {
141
            $model = new $name();
142
            $rels = $model->getRelationships();
143
            // it doesn't matter if a model has no relationships here, that lack will simply be skipped over
144
            // during hookup processing
145
            $hooks[$name] = $rels;
146
        }
147
148
        // models with non-empty relation gubbins are assembled, now the hard bit starts
149
        // storing assembled bidirectional relationship schema
150
        $rawLines = [];
151
        // storing unprocessed relation gubbins for second-pass processing
152
        $remix = [];
153
        $this->calculateRoundTripRelationsFirstPass($hooks, $rawLines, $remix);
154
155
        // now for second processing pass, to pick up stuff that first didn't handle
156
        $rawLines = $this->calculateRoundTripRelationsSecondPass($remix, $rawLines);
157
158
        // now deduplicate rawLines - can't use array_unique as array value elements are themselves arrays
159
        $lines = [];
160
        foreach ($rawLines as $line) {
161
            if (!in_array($line, $lines)) {
162
                $lines[] = $line;
163
            }
164
        }
165
166
        return $lines;
167
    }
168
169
    /**
170
     * @param $remix
171
     * @param $lines
172
     * @return array
173
     */
174
    private function calculateRoundTripRelationsSecondPass($remix, $lines)
175
    {
176
        foreach ($remix as $principalType => $value) {
177
            foreach ($value as $fk => $localRels) {
178
                foreach ($localRels as $dependentType => $deets) {
179
                    $principalMult = $deets['multiplicity'];
180
                    $principalProperty = $deets['property'];
181
                    $principalKey = $deets['local'];
182
183
                    if (!isset($remix[$dependentType])) {
184
                        continue;
185
                    }
186
                    $foreign = $remix[$dependentType];
187
                    if (!isset($foreign[$principalKey])) {
188
                        continue;
189
                    }
190
                    $foreign = $foreign[$principalKey];
191
                    $dependentKey = $foreign[$dependentType]['local'];
192
                    if ($fk != $dependentKey) {
193
                        continue;
194
                    }
195
                    $dependentMult = $foreign[$dependentType]['multiplicity'];
196
                    $dependentProperty = $foreign[$dependentType]['property'];
197
                    assert(
198
                        in_array($dependentMult, $this->multConstraints[$principalMult]),
199
                        "Cannot pair multiplicities " . $dependentMult . " and " . $principalMult
200
                    );
201
                    assert(
202
                        in_array($principalMult, $this->multConstraints[$dependentMult]),
203
                        "Cannot pair multiplicities " . $principalMult . " and " . $dependentMult
204
                    );
205
                    // generate forward and reverse relations
206
                    list($forward, $reverse) = $this->calculateRoundTripRelationsGenForwardReverse(
207
                        $principalType,
208
                        $principalMult,
209
                        $principalProperty,
210
                        $dependentType,
211
                        $dependentMult,
212
                        $dependentProperty
213
                    );
214
                    // add forward relation
215
                    $lines[] = $forward;
216
                    // add reverse relation
217
                    $lines[] = $reverse;
218
                }
219
            }
220
        }
221
        return $lines;
222
    }
223
224
    /**
225
     * @param $hooks
226
     * @param $lines
227
     * @param $remix
228
     */
229
    private function calculateRoundTripRelationsFirstPass($hooks, &$lines, &$remix)
230
    {
231
        foreach ($hooks as $principalType => $value) {
232
            foreach ($value as $fk => $localRels) {
233
                foreach ($localRels as $dependentType => $deets) {
234
                    $principalMult = $deets['multiplicity'];
235
                    $principalProperty = $deets['property'];
236
                    $principalKey = $deets['local'];
237
238
                    $foreign = $hooks[$dependentType];
239
                    $foreign = null != $foreign && isset($foreign[$principalKey]) ? $foreign[$principalKey] : null;
240
241
                    if (null != $foreign && isset($foreign[$principalType])) {
242
                        $foreign = $foreign[$principalType];
243
                        $dependentMult = $foreign['multiplicity'];
244
                        $dependentProperty = $foreign['property'];
245
                        assert(
246
                            in_array($dependentMult, $this->multConstraints[$principalMult]),
247
                            "Cannot pair multiplicities " . $dependentMult . " and " . $principalMult
248
                        );
249
                        assert(
250
                            in_array($principalMult, $this->multConstraints[$dependentMult]),
251
                            "Cannot pair multiplicities " . $principalMult . " and " . $dependentMult
252
                        );
253
                        // generate forward and reverse relations
254
                        list($forward, $reverse) = $this->calculateRoundTripRelationsGenForwardReverse(
255
                            $principalType,
256
                            $principalMult,
257
                            $principalProperty,
258
                            $dependentType,
259
                            $dependentMult,
260
                            $dependentProperty
261
                        );
262
                        // add forward relation
263
                        $lines[] = $forward;
264
                        // add reverse relation
265
                        $lines[] = $reverse;
266
                    } else {
267
                        if (!isset($remix[$principalType])) {
268
                            $remix[$principalType] = [];
269
                        }
270
                        if (!isset($remix[$principalType][$fk])) {
271
                            $remix[$principalType][$fk] = [];
272
                        }
273
                        if (!isset($remix[$principalType][$fk][$dependentType])) {
274
                            $remix[$principalType][$fk][$dependentType] = $deets;
275
                        }
276
                        assert(isset($remix[$principalType][$fk][$dependentType]));
277
                    }
278
                }
279
            }
280
        }
281
    }
282
283
    /**
284
     * @param $principalType
285
     * @param $principalMult
286
     * @param $principalProperty
287
     * @param $dependentType
288
     * @param $dependentMult
289
     * @param $dependentProperty
290
     * @return array
291
     */
292
    private function calculateRoundTripRelationsGenForwardReverse(
293
        $principalType,
294
        $principalMult,
295
        $principalProperty,
296
        $dependentType,
297
        $dependentMult,
298
        $dependentProperty
299
    ) {
300
        $forward = [
301
            'principalType' => $principalType,
302
            'principalMult' => $principalMult,
303
            'principalProp' => $principalProperty,
304
            'dependentType' => $dependentType,
305
            'dependentMult' => $dependentMult,
306
            'dependentProp' => $dependentProperty
307
        ];
308
        $reverse = [
309
            'principalType' => $dependentType,
310
            'principalMult' => $dependentMult,
311
            'principalProp' => $dependentProperty,
312
            'dependentType' => $principalType,
313
            'dependentMult' => $principalMult,
314
            'dependentProp' => $principalProperty
315
        ];
316
        return array($forward, $reverse);
317
    }
318
}
319