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

MetadataProvider::boot()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 31.9476

Importance

Changes 7
Bugs 0 Features 0
Metric Value
c 7
b 0
f 0
dl 0
loc 38
ccs 3
cts 29
cp 0.1034
rs 8.439
cc 6
eloc 23
nc 5
nop 0
crap 31.9476
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
            if (0 < count($rels)) {
144
                $hooks[$name] = $rels;
145
            }
146
        }
147
148
        // models with non-empty relation gubbins are assembled, now the hard bit starts
149
        // storing assembled bidirectional relationship schema
150
        $lines = [];
151
        // storing unprocessed relation gubbins for second-pass processing
152
        $remix = [];
153
        foreach ($hooks as $principalType => $value) {
154
            foreach ($value as $fk => $localRels) {
155
                foreach ($localRels as $dependentType => $deets) {
156
                    $principalMult = $deets['multiplicity'];
157
                    $principalProperty = $deets['property'];
158
                    $principalKey = $deets['local'];
159
160
                    $foreign = $hooks[$dependentType];
161
                    $foreign = null != $foreign && isset($foreign[$principalKey]) ? $foreign[$principalKey] : null;
162
163
                    if (null != $foreign && isset($foreign[$principalType])) {
164
                        $foreign = $foreign[$principalType];
165
                        $dependentMult = $foreign['multiplicity'];
166
                        $dependentProperty = $foreign['property'];
167
                        assert(
168
                            in_array($dependentMult, $this->multConstraints[$principalMult]),
169
                            "Cannot pair multiplicities ".$dependentMult. " and ".$principalMult
170
                        );
171
                        assert(
172
                            in_array($principalMult, $this->multConstraints[$dependentMult]),
173
                            "Cannot pair multiplicities ".$principalMult. " and ".$dependentMult
174
                        );
175
                        // add forward relation
176
                        $forward = ['principalType' => $principalType, 'principalMult' => $principalMult,
177
                            'principalProp' => $principalProperty, 'dependentType' => $dependentType,
178
                            'dependentMult' => $dependentMult, 'dependentProp' => $dependentProperty];
179
                        if (!in_array($forward, $lines)) {
180
                            $lines[] = $forward;
181
                        }
182
                        // add reverse relation
183
                        $reverse = ['principalType' => $dependentType, 'principalMult' => $dependentMult,
184
                            'principalProp' => $dependentProperty, 'dependentType' => $principalType,
185
                            'dependentMult' => $principalMult, 'dependentProp' => $principalProperty];
186
                        if (!in_array($reverse, $lines)) {
187
                            $lines[] = $reverse;
188
                        }
189
                    } else {
190
                        if (!isset($remix[$principalType])) {
191
                            $remix[$principalType] = [];
192
                        }
193
                        if (!isset($remix[$principalType][$fk])) {
194
                            $remix[$principalType][$fk] = [];
195
                        }
196
                        if (!isset($remix[$principalType][$fk][$dependentType])) {
197
                            $remix[$principalType][$fk][$dependentType] = $deets;
198
                        }
199
                        assert(isset($remix[$principalType][$fk][$dependentType]));
200
                    }
201
                }
202
            }
203
        }
204
205
        // now for second processing pass, to pick up stuff that first didn't handle
206
        foreach ($remix as $principalType => $value) {
207
            foreach ($value as $fk => $localRels) {
208
                foreach ($localRels as $dependentType => $deets) {
209
                    $principalMult = $deets['multiplicity'];
210
                    $principalProperty = $deets['property'];
211
                    $principalKey = $deets['local'];
212
213
                    if (!isset($remix[$dependentType])) {
214
                        continue;
215
                    }
216
                    $foreign = $remix[$dependentType];
217
                    if (!isset($foreign[$principalKey])) {
218
                        continue;
219
                    }
220
                    $foreign = $foreign[$principalKey];
221
                    $dependentKey = $foreign[$dependentType]['local'];
222
                    if ($fk != $dependentKey) {
223
                        continue;
224
                    }
225
                    $dependentMult = $foreign[$dependentType]['multiplicity'];
226
                    $dependentProperty = $foreign[$dependentType]['property'];
227
                    assert(
228
                        in_array($dependentMult, $this->multConstraints[$principalMult]),
229
                        "Cannot pair multiplicities ".$dependentMult. " and ".$principalMult
230
                    );
231
                    assert(
232
                        in_array($principalMult, $this->multConstraints[$dependentMult]),
233
                        "Cannot pair multiplicities ".$principalMult. " and ".$dependentMult
234
                    );
235
                    // add forward relation
236
                    $forward = ['principalType' => $principalType, 'principalMult' => $principalMult,
237
                        'principalProp' => $principalProperty, 'dependentType' => $dependentType,
238
                        'dependentMult' => $dependentMult, 'dependentProp' => $dependentProperty];
239
                    if (!in_array($forward, $lines)) {
240
                        $lines[] = $forward;
241
                    }
242
                    // add reverse relation
243
                    $reverse = ['principalType' => $dependentType, 'principalMult' => $dependentMult,
244
                        'principalProp' => $dependentProperty, 'dependentType' => $principalType,
245
                        'dependentMult' => $principalMult, 'dependentProp' => $principalProperty];
246
                    if (!in_array($reverse, $lines)) {
247
                        $lines[] = $reverse;
248
                    }
249
                }
250
            }
251
        }
252
253
        return $lines;
254
    }
255
}
256