Passed
Pull Request — master (#172)
by Alex
03:49
created

MetadataControllerProvider::getAppNamespace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 6
1
<?php
2
3
namespace AlgoWeb\PODataLaravel\Providers;
4
5
use AlgoWeb\PODataLaravel\Controllers\MetadataControllerContainer;
6
use AlgoWeb\PODataLaravel\Controllers\MetadataControllerTrait;
7
use Illuminate\Routing\Controller;
8
use Illuminate\Support\Facades\App;
9
use Illuminate\Support\Facades\Cache;
10
use POData\Common\InvalidOperationException;
11
12
class MetadataControllerProvider extends MetadataBaseProvider
13
{
14
    /*
15
     * Optional crud verbs - these need to be deduplicated for empty mappings
16 68
     */
17
    protected $optionalVerbs = ['bulkCreate', 'bulkUpdate'];
18 68
19
    /**
20 68
     * Bootstrap the application services.  Post-boot.
21
     *
22
     * @return void
23
     */
24
    public function boot()
25
    {
26 68
        $isCaching = true === $this->getIsCaching();
27
        $hasCache = null;
28 68
29 68
        if ($isCaching) {
30 68
            $hasCache = Cache::has('metadataControllers');
31 68
            if ($hasCache) {
32 68
                $meta = Cache::get('metadataControllers');
33 68
                App::instance('metadataControllers', $meta);
34 68
                return;
35
            }
36 68
        }
37 68
38 68
        $meta = App::make('metadataControllers');
39 68
40
        $classes = $this->getClassMap();
41 68
        $ends = $this->getCandidateControllers($classes);
42 68
43 68
        // now process each class that uses the metadata controller trait and stick results in $metamix
44 68
        $metamix = [];
45 67
        foreach ($ends as $end) {
46 67
            $map = $end->getMappings();
47 68
            // verify uniqueness - must be exactly one mapping for model-verb combo - different verb mappings for
48
            // a model can glom onto different controllers
49
            foreach ($map as $key => $lock) {
50 68
                if (!array_key_exists($key, $metamix)) {
51
                    // if we haven't yet got a mapping for this model, grab it holus-bolus
52
                    $metamix[$key] = $lock;
53 68
                    continue;
54 68
                }
55 67
                // if we do, make sure we aren't re-adding mappings for any of the CRUD verbs
56
                foreach ($lock as $barrel => $roll) {
57
                    $isOptional = in_array($barrel, $this->optionalVerbs);
58 67
                    $alreadyKey = array_key_exists($barrel, $metamix[$key]);
59 67
                    if ($isOptional) {
60
                        // if we've picked up a default mapping for an optional verb, then we can overwrite it
61 67
                        $alreadyKey = null === $metamix[$key][$barrel] ? false : $alreadyKey;
62 67
                    }
63
                    if ($alreadyKey) {
64
                        $msg = 'Mapping already defined for model ' . $key . ' and CRUD verb ' . $barrel;
65
                        throw new InvalidOperationException($msg);
66
                    }
67
                    $metamix[$key][$barrel] = $roll;
68
                }
69
            }
70
        }
71
72
        $meta->setMetadata($metamix);
73 67
74 68
        $key = 'metadataControllers';
75
        $this->handlePostBoot($isCaching, $hasCache, $key, $meta);
76 68
    }
77
78 68
    /**
79
     * Register the application services.  Boot-time only.
80
     *
81
     * @return void
82
     */
83
    public function register()
84
    {
85
        $this->app->singleton(
86
            'metadataControllers',
87 68
            function ($app) {
0 ignored issues
show
Unused Code introduced by
The parameter $app is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

87
            function (/** @scrutinizer ignore-unused */ $app) {

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

Loading history...
88 68
                return new MetadataControllerContainer();
89 68
            }
90 68
        );
91
    }
92
93
    /**
94
     * @param $classes
95
     * @throws \Exception
96
     * @return array
97
     */
98
    protected function getCandidateControllers($classes)
99
    {
100
        $ends = [];
101
        $startName = $this->getAppNamespace();
102
        $rawClasses = [];
103
        foreach ($classes as $name) {
104
            // not in app namespace, keep moving
105
            if (!\Illuminate\Support\Str::startsWith($name, $startName)) {
106
                continue;
107
            }
108
            // if class doesn't exist (for whatever reason), skip it now rather than muck about later
109
            if (!class_exists($name)) {
110
                continue;
111
            }
112
            $rawClasses[] = $name;
113
        }
114
115
        foreach ($rawClasses as $name) {
116
            // not in app namespace, keep moving
117
            if (!\Illuminate\Support\Str::startsWith($name, $startName)) {
118
                continue;
119
            }
120
            // if class doesn't exist (for whatever reason), skip it now rather than muck about later
121
            if (!class_exists($name)) {
122
                continue;
123
            }
124
            try {
125
                if (in_array(MetadataControllerTrait::class, class_uses($name, false))) {
126
                    $result = App::make($name);
127
                    $ends[] = $result;
128
                    if (!$result instanceof Controller) {
129
                        throw new InvalidOperationException('Resolved result not a controller');
130
                    }
131
                }
132
            } catch (\Exception $e) {
133
                if (!App::runningInConsole()) {
134
                    throw $e;
135
                }
136
                // Squash exceptions thrown here when running from CLI so app can continue booting
137
            }
138
        }
139
        return $ends;
140
    }
141
142
    protected function getAppNamespace()
143
    {
144
        $startName = defined('PODATA_LARAVEL_APP_ROOT_NAMESPACE') ? PODATA_LARAVEL_APP_ROOT_NAMESPACE : 'App';
0 ignored issues
show
Bug introduced by
The constant AlgoWeb\PODataLaravel\Pr...AVEL_APP_ROOT_NAMESPACE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
145
        return $startName;
146
    }
147
}
148