MetadataControllerTrait   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 184
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 26
eloc 79
c 2
b 0
f 0
dl 0
loc 184
rs 10
ccs 66
cts 66
cp 1

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getParameterNames() 0 22 3
B getMappings() 0 51 11
B getMethodName() 0 48 9
A checkCrudVerbDefined() 0 5 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AlgoWeb\PODataLaravel\Controllers;
6
7
use Illuminate\Routing\Controller as BaseController;
8
use POData\Common\InvalidOperationException;
9
10
trait MetadataControllerTrait
11
{
12
    /*
13
     * Allowed crud verbs
14
     */
15
    protected $crudVerbs = ['create', 'read', 'update', 'delete'];
16
17
    /*
18
     * Optional crud verbs - if these are unset in mapping array, LaravelQuery drops through to default handler
19
     */
20
    protected $optionalVerbs = ['bulkCreate', 'bulkUpdate'];
21
22
    /*
23
     * Array to record mapping betweeen model-verb combos and names
24
     * First-level key is fully-qualified model name
25
     * (eg Alt\Swedish\Chef\Bork\Bork\Bork)
26
     * Second-level key is CRUD verb
27
     */
28
    protected $mapping;
29
30 9
    /*
31
     * Given model and verb, get method name and parameter list
32
     *
33 9
     * @param string $modelName
34
     * @param $crudVerb
35 9
     * @return null|array
36
     * @throws \Exception
37 8
     */
38 1
    /**
39
     * @param string $modelName
40
     * @param $crudVerb
41 7
     * @throws InvalidOperationException
42 1
     * @throws \ReflectionException
43
     * @throws \Exception
44
     * @return array
45 6
     */
46 6
    public function getMethodName($modelName, $crudVerb)
47 1
    {
48
        // enforce we're actually hooked up to a controller
49
        if (!$this instanceof BaseController) {
50 5
            throw new InvalidOperationException(get_class($this));
51 1
        }
52
        // enforce that mapping is actually not empty
53 4
        if (0 == count($this->mapping)) {
54 4
            throw new InvalidOperationException('Mapping array must not be empty');
55 1
        }
56
57
        if (!array_key_exists($modelName, $this->mapping)) {
58 3
            $msg = 'Metadata mapping for model ' . $modelName . ' not defined';
59 1
            throw new \Exception($msg);
60 1
        }
61 1
62
        $this->checkCrudVerbDefined($crudVerb);
63
        $isOptional = in_array($crudVerb, $this->optionalVerbs);
64 2
65 2
        $lookup = $this->mapping[$modelName];
66
        if (!is_array($lookup)) {
67 2
            $msg = 'Metadata mapping for model ' . $modelName . ' not an array';
68
            throw new \Exception($msg);
69
        }
70 68
71
        if (!array_key_exists($crudVerb, $lookup)) {
72
            if ($isOptional) {
73 68
                // optional crud verbs don't have to be defined - so we can return null
74
                return null;
75 68
            }
76
            $msg = 'Metadata mapping for CRUD verb ' . $crudVerb . ' on model ' . $modelName . ' not defined';
77 68
            throw new \Exception($msg);
78
        }
79
        $result = $lookup[$crudVerb];
80 68
        if (!isset($result)) {
81 68
            $msg = 'Metadata mapping for CRUD verb ' . $crudVerb . ' on model ' . $modelName . ' null';
82 1
            throw new \Exception($msg);
83
        }
84 68
85 68
        if (!method_exists($this, $result)) {
86 1
            $msg = 'Metadata target for CRUD verb ' . $crudVerb . ' on model ' . $modelName . ' does not exist';
87
            throw new \Exception($msg);
88 68
        }
89 1
90
        $class     = get_class($this);
91
        $parmArray = $this->getParameterNames($result);
92 68
93 1
        return ['method' => $result, 'controller' => $class, 'parameters' => $parmArray];
94 1
    }
95 1
96
    /**
97 68
     * @throws InvalidOperationException
98 68
     * @throws \ReflectionException
99 68
     * @throws \Exception
100 68
     * @return array
101
     */
102 68
    public function getMappings()
103 68
    {
104 68
        // enforce we're actually hooked up to a controller
105 68
        if (!$this instanceof BaseController) {
106 68
            throw new InvalidOperationException(get_class($this));
107
        }
108
        // enforce that mapping is actually not empty
109
        if (empty($this->mapping)) {
110
            throw new InvalidOperationException('Mapping array must not be empty');
111
        }
112
113 68
114
        $allMappings = [];
115 68
116 68
        // check that mapping array is well formed and sane, rather than waiting to stab us with a spatula
117 68
        foreach ($this->mapping as $key => $map) {
118 68
            if (!is_array($map)) {
119 68
                $msg = 'Metadata mapping for model ' . $key . ' not an array';
120 68
                throw new \Exception($msg);
121 68
            }
122 68
            foreach ($map as $verb => $method) {
123 68
                $this->checkCrudVerbDefined($verb);
124
                if (!isset($method)) {
125 68
                    $msg = 'Metadata mapping for CRUD verb ' . $verb . ' on model ' . $key . ' null';
126 68
                    throw new \Exception($msg);
127 68
                }
128 68
129 68
                if (!method_exists($this, $method)) {
130
                    $msg = 'Metadata target for CRUD verb ' . $verb . ' on model ' . $key . ' does not exist';
131 68
                    throw new \Exception($msg);
132 68
                }
133
                $parmArray = $this->getParameterNames($method);
134 68
                if (!array_key_exists($key, $allMappings)) {
135 68
                    $allMappings[$key] = [];
136
                }
137
138
                $class                    = get_class($this);
139
                $allMappings[$key][$verb] = ['method' => $method, 'controller' => $class, 'parameters' => $parmArray];
140
            }
141
        }
142
        // bolt on optional, undefined mappings - empty mappings will need to be deduplicated in metadata controller
143
        // provider
144
        $mapKeys = array_keys($this->mapping);
145
        foreach ($mapKeys as $map) {
146
            $undefined = array_diff($this->optionalVerbs, array_keys($this->mapping[$map]));
147
            foreach ($undefined as $undef) {
148
                $allMappings[$map][$undef] = null;
149
            }
150
        }
151
152
        return $allMappings;
153
    }
154
155
    /**
156
     * @param  string               $result
157
     * @throws \ReflectionException
158
     * @return array
159
     */
160
    protected function getParameterNames(string $result)
161
    {
162
        $parmArray = [];
163
        $reflec    = new \ReflectionMethod($this, $result);
164
        $params    = $reflec->getParameters();
165
        foreach ($params as $parm) {
166
            $detail         = [];
167
            $detail['name'] = $parm->name;
168
            $classHint      = $parm->getClass();
169
            $isRequest      = false;
170
            if (null != $classHint) {
171
                // check to see if this is a request
172
                $className      = $classHint->name;
173
                $class          = new $className();
174
                $isRequest      = $class instanceof \Illuminate\Http\Request;
175
                $detail['type'] = $className;
176
            }
177
178
            $detail['isRequest']    = $isRequest;
179
            $parmArray[$parm->name] = $detail;
180
        }
181
        return $parmArray;
182
    }
183
184
    /**
185
     * @param string $crudVerb
186
     *
187
     * @throws \Exception
188
     */
189
    private function checkCrudVerbDefined(string $crudVerb)
190
    {
191
        if (!in_array($crudVerb, $this->crudVerbs) && !in_array($crudVerb, $this->optionalVerbs)) {
192
            $msg = 'CRUD verb ' . $crudVerb . ' not defined';
193
            throw new \Exception($msg);
194
        }
195
    }
196
}
197