Passed
Branch dev-master (8c1809)
by Vijay
02:47
created

Mapper   B

Complexity

Total Complexity 54

Size/Duplication

Total Lines 327
Duplicated Lines 23.55 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 77
loc 327
rs 7.0642
c 0
b 0
f 0
wmc 54
lcom 1
cbo 6

10 Methods

Rating   Name   Duplication   Size   Complexity  
D getIdObjectIfAdmin() 4 36 9
B get() 14 14 7
B delete() 0 20 6
A listingAdmin() 11 11 2
C __construct() 0 31 8
A getMapper() 0 4 1
C getIdObjectIfUser() 4 33 7
B listing() 17 17 6
A searchAdmin() 11 11 2
B search() 16 16 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Mapper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Mapper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace FFCMS\Controllers\API;
4
5
use FFMVC\Helpers;
6
use FFCMS\{Traits, Models, Mappers};
7
8
9
/**
10
 * REST API Base Controller Class.
11
 *
12
 * @author Vijay Mahrra <[email protected]>
13
 * @copyright Vijay Mahrra
14
 * @license GPLv3 (http://www.gnu.org/licenses/gpl-3.0.html)
15
 */
16
abstract class Mapper extends API
17
{
18
    use Traits\Validation,
19
        Traits\SearchController;
20
21
    /**
22
     * For admin listing and search results
23
     */
24
    use Traits\SearchController;
25
26
    /**
27
     * @var object database class
28
     */
29
    protected $db;
30
31
    /**
32
     * @var string table in the db
33
     */
34
    protected $table = null;
35
36
    /**
37
     * @var string class name
38
     */
39
    protected $mapperClass;
40
41
    /**
42
     * @var \FFMVC\Mappers\Mapper mapper for class
43
     */
44
    protected $mapper;
45
46
    /**
47
     * is the user authorised for access?
48
     *
49
     * @var bool
50
     */
51
    protected $isAuthorised = false;
52
53
    /**
54
     * is the user authorised for access to the object type?
55
     *
56
     * @var bool
57
     */
58
    protected $adminOnly = true;
59
60
61
    /**
62
     * initialize
63
     * @param \Base $f3
64
     */
65
    public function __construct(\Base $f3)
66
    {
67
        parent::__construct($f3);
68
69
        // guess the table name from the class name if not specified as a class member
70
        $class = \UTF::instance()->substr(strrchr(get_class($this), '\\'),1);
71
        $this->table = empty($this->table) ? $f3->snakecase($class) : $this->table;
72
        $mapperClass = "\FFCMS\Mappers\\" . $class;
73
74
        if (class_exists($mapperClass) && $this instanceof Mapper) {
75
            $this->mapperClass = $mapperClass;
76
            $this->mapper = new $this->mapperClass;
77
        }
78
79
        $this->isAuthorised = $this->validateAccess();
80
        if (empty($this->isAuthorised)) {
81
            $this->setOAuthError('invalid_grant');
82
        }
83
84
        $deny = false;
85
        $isAdmin = $f3->get('isAdmin');
86
        if (!$isAdmin && !empty($this->adminOnly)) {
87
            $deny = true;
88
        }
89
90
        $this->isAuthorised = empty($deny);
91
        if ($deny) {
92
            $this->failure('authentication_error', "User does not have permission.", 401);
93
            $this->setOAuthError('access_denied');
94
        }
95
    }
96
97
98
    /**
99
     * Get the associated mapper for the table
100
     *
101
     * @return
102
     */
103
    public function &getMapper()
104
    {
105
        return $this->mapper;
106
    }
107
108
109
    /**
110
     * Check permissions and load the mapper with the object in the URL param @id
111
     * for the user
112
     *
113
     * @param \Base $f3
114
     * @param array $params
115
     * @param string $idField the field used for the unique id to load by
116
     * @param string|null $defaultId defaule value to use if not found
117
     * @return null|array|boolean|\FFMVC\Mappers\Mapper
118
     */
119
    public function getIdObjectIfUser(\Base $f3, array $params, string $idField = 'uuid', $defaultId = null)
120
    {
121
        // valid user?
122
        if (empty($this->isAuthorised)) {
123
            return;
124
        }
125
126
        // only admin has permission to specify @id param
127
        $isAdmin = $f3->get('isAdmin');
128
        $id = !empty($params['id']) ? $params['id'] : $f3->get('REQUEST.id');
129
130
        if ((!$isAdmin && !empty($this->adminOnly))) {
131
            $this->failure('authentication_error', "User does not have permission.", 401);
132
            return $this->setOAuthError('access_denied');
133
        }
134
135
        // use default user id
136
        if (empty($id)) {
137
            $id = $defaultId;
138
        }
139
140
        // load object by correct id
141
        $db = \Registry::get('db');
142
        $m = $this->getMapper();
143
        $m->load([$db->quotekey($idField) . ' = ?', $id]);
144 View Code Duplication
        if (null == $m->$idField) {
145
            $this->failure('authentication_error', "Object with @id does not exist.", 404);
146
            return $this->setOAuthError('invalid_request');
147
        }
148
149
        $this->mapper =& $m;
150
        return $m;
151
    }
152
153
154
    /**
155
     * Check permissions and load the mapper with the object in the URL param @id
156
     * if the user is an admin
157
     *
158
     * @param \Base $f3
159
     * @param array $params
160
     * @param string $idField the field used for the unique id to load by
161
     * @param string|null $defaultId defaule value to use if not found
162
     * @return null|array|boolean|\FFMVC\Mappers\Mapper
163
     */
164
    public function getIdObjectIfAdmin(\Base $f3, array $params, string $idField = 'uuid', $defaultId = null)
165
    {
166
        if (empty($this->isAuthorised)) {
167
            return;
168
        }
169
170
        // only admin has permission to delete @id
171
        $isAdmin = $f3->get('isAdmin');
172
        if (!$isAdmin) {
173
            $this->failure('authentication_error', "User does not have permission.", 401);
174
            return $this->setOAuthError('access_denied');
175
        }
176
177
        // invalid id
178
        $id = !empty($params['id']) ? $params['id'] : $f3->get('REQUEST.id');
179
        if (empty($id)) {
180
            $id = $defaultId;
181
        }
182
183
        if (!empty($id) && ('uuid' == $idField && 36 !== strlen($id))) {
184
            $this->failure('authentication_error', "Invalid @id parameter.", 400);
185
            return $this->setOAuthError('invalid_request');
186
        }
187
188
        // check id exists
189
        $db = \Registry::get('db');
190
        $m = $this->getMapper();
191
        $m->load([$db->quotekey($idField) . ' = ?', $id]);
192 View Code Duplication
        if (null == $m->$idField) {
193
            $this->failure('authentication_error', "Object with @id does not exist.", 404);
194
            return $this->setOAuthError('invalid_request');
195
        }
196
197
        $this->mapper =& $m;
198
        return $m;
199
    }
200
201
202
    /**
203
     * Display the data item
204
     *
205
     * @param \Base $f3
206
     * @param array $params
207
     * @return null|array|boolean
208
     */
209 View Code Duplication
    public function get(\Base $f3, array $params)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
    {
211
        $isAdmin = $f3->get('isAdmin');
212
        $m = $this->getIdObjectIfUser($f3, $params, 'uuid', $params['id']);
213
        if (!is_object($m) || null == $m->uuid) {
214
            return;
215
        } elseif (!$isAdmin && $m->users_uuid !== $f3->get('uuid')) {
216
            $this->failure('authentication_error', "User does not have permission.", 401);
217
            return $this->setOAuthError('access_denied');
218
        }
219
        // return raw data for object?
220
        $adminView = $f3->get('isAdmin') && 'admin' == $f3->get('REQUEST.view');
221
        $this->data = $adminView ? $m->castFields($f3->get('REQUEST.fields')) : $m->exportArray($f3->get('REQUEST.fields'));
222
    }
223
224
225
    /**
226
     * Delete the data object indicated by @id in the request
227
     *
228
     * @param \Base $f3
229
     * @param array $params
230
     * @return null|array|boolean
231
     */
232
    public function delete(\Base $f3, array $params)
233
    {
234
        $m = $this->getIdObjectIfUser($f3, $params);
235
        if (!is_object($m) || null == $m->uuid) {
236
            return;
237
        }
238
239
        // a user can only delete if they own the object
240
        $isAdmin = $f3->get('isAdmin');
241
        if (!$isAdmin) {
242
            if (!empty($m->users_uuid) && $m->users_uuid !== $f3->get('uuid')) {
243
                $this->failure('authentication_error', "User does not have permission.", 401);
244
                return $this->setOAuthError('access_denied');
245
            }
246
        }
247
248
        $this->data = [
249
            'deleted' => $m->erase()
250
        ];
251
    }
252
253
254
    /**
255
     * list objects (list is a reserved keyword)
256
     *
257
     * @param \Base $f3
258
     * @return null|array|boolean
259
     */
260 View Code Duplication
    public function listingAdmin(\Base $f3)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
261
    {
262
        // must be an admin
263
        $isAdmin = $f3->get('isAdmin');
264
        if (!$isAdmin) {
265
            $this->failure('authentication_error', "User does not have permission.", 401);
266
            return $this->setOAuthError('access_denied');
267
        }
268
269
        $this->data = $this->getListingResults($f3, $this->getMapper());
270
    }
271
272
273
    /**
274
     * list objects (list is a reserved keyword)
275
     *
276
     * @param \Base $f3
277
     * @param array $params
278
     * @return array|boolean|null
279
     */
280 View Code Duplication
    public function listing(\Base $f3, array $params)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281
    {
282
        $isAdmin = $f3->get('isAdmin');
283
        $users_uuid = null;
0 ignored issues
show
Unused Code introduced by
$users_uuid 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...
284
        if (!$isAdmin && array_key_exists('id', $params)) {
285
            $this->failure('authentication_error', "User does not have permission.", 401);
286
            return $this->setOAuthError('access_denied');
287
        } elseif ($isAdmin && array_key_exists('id', $params)) {
288
            $users_uuid = $params['id'];
289
        } elseif (!$isAdmin) {
290
            $users_uuid = $f3->get('uuid');
291
        } else {
292
            $users_uuid = null;
293
        }
294
295
        $this->data = $this->getListingResults($f3, $this->getMapper(), $users_uuid);
296
    }
297
298
299
    /**
300
     * search objects
301
     *
302
     * @param \Base $f3
303
     * @return null|array|boolean
304
     */
305 View Code Duplication
    public function searchAdmin(\Base $f3)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306
    {
307
        // must be an admin
308
        $isAdmin = $f3->get('isAdmin');
309
        if (!$isAdmin) {
310
            $this->failure('authentication_error', "User does not have permission.", 401);
311
            return $this->setOAuthError('access_denied');
312
        }
313
314
        $this->data = $this->getSearchResults($f3, $this->getMapper());
315
    }
316
317
318
    /**
319
     * search objects
320
     *
321
     * @param \Base $f3
322
     * @param array $params
323
     * @return null|array|boolean
324
     */
325 View Code Duplication
    public function search(\Base $f3, array $params)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
326
    {
327
        $isAdmin = $f3->get('isAdmin');
328
        if (!$isAdmin && array_key_exists('id', $params)) {
329
            $this->failure('authentication_error', "User does not have permission.", 401);
330
            return $this->setOAuthError('access_denied');
331
        } elseif ($isAdmin && array_key_exists('id', $params)) {
332
            $users_uuid = $params['id'];
333
        } elseif (!$isAdmin) {
334
            $users_uuid = $f3->get('uuid');
335
        } else {
336
            $users_uuid = null;
337
        }
338
339
        $this->data = $this->getSearchResults($f3, $this->getMapper(), $users_uuid);
340
    }
341
342
}
343