Passed
Pull Request — master (#154)
by Alex
07:05
created

MetadataControllerTrait::getMappings()   C

Complexity

Conditions 9
Paths 15

Size

Total Lines 46
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 9

Importance

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

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

164
        /** @scrutinizer ignore-call */ 
165
        assert(is_string($crudVerb));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
165
        $lowVerb = strtolower($crudVerb);
166
        if (!in_array($lowVerb, $this->crudVerbs) && !in_array($crudVerb, $this->optionalVerbs)) {
167
            $msg = 'CRUD verb ' . $crudVerb . ' not defined';
168
            throw new \Exception($msg);
169
        }
170
    }
171
}
172