Completed
Push — master ( 72d72a...bb8091 )
by Yaro
06:09
created

AbstractTableController::__call()   C

Complexity

Conditions 15
Paths 86

Size

Total Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 17.9312

Importance

Changes 0
Metric Value
dl 0
loc 45
ccs 26
cts 34
cp 0.7647
rs 5.9166
c 0
b 0
f 0
cc 15
nc 86
nop 2
crap 17.9312

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Yaro\Jarboe\Http\Controllers;
4
5
use Illuminate\Foundation\Validation\ValidatesRequests;
6
use Illuminate\Http\Request;
7
use Illuminate\Support\Facades\Request as RequestFacade;
8
use Illuminate\Validation\ValidationException;
9
use Spatie\Permission\Exceptions\UnauthorizedException;
10
use Yaro\Jarboe\Http\Controllers\Traits\AliasesTrait;
11
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\CreateHandlerTrait;
12
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\DeleteHandlerTrait;
13
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\EditHandlerTrait;
14
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\ForceDeleteHandlerTrait;
15
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\InlineHandlerTrait;
16
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\ListHandlerTrait;
17
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\OrderByHandlerTrait;
18
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\PerPageHandlerTrait;
19
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\RestoreHandlerTrait;
20
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\SearchHandlerTrait;
21
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\SearchRelationHandlerTrait;
22
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\SortableHandlerTrait;
23
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\StoreHandlerTrait;
24
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\ToolbarHandlerTrait;
25
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\UpdateHandlerTrait;
26
use Yaro\Jarboe\Http\Controllers\Traits\NotifyTrait;
27
use Yaro\Jarboe\Table\Actions\CreateAction;
28
use Yaro\Jarboe\Table\Actions\DeleteAction;
29
use Yaro\Jarboe\Table\Actions\EditAction;
30
use Yaro\Jarboe\Table\Actions\ForceDeleteAction;
31
use Yaro\Jarboe\Table\Actions\RestoreAction;
32
use Yaro\Jarboe\Table\CRUD;
33
use Yaro\Jarboe\Table\Fields\AbstractField;
34
use Yaro\Jarboe\Table\Toolbar\TranslationLocalesSelectorTool;
35
36
/**
37
 * @method mixed list(Request $request)
38
 * @method mixed search(Request $request)
39
 * @method mixed create(Request $request)
40
 * @method mixed store(Request $request)
41
 * @method mixed edit(Request $request, $id)
42
 * @method mixed update(Request $request, $id)
43
 * @method mixed delete(Request $request, $id)
44
 * @method mixed restore(Request $request, $id)
45
 * @method mixed forceDelete(Request $request, $id)
46
 * @method mixed inline(Request $request)
47
 */
48
abstract class AbstractTableController
49
{
50
    use ValidatesRequests;
51
    use NotifyTrait;
52
    use AliasesTrait;
53
    use DeleteHandlerTrait;
54
    use ToolbarHandlerTrait;
55
    use InlineHandlerTrait;
56
    use ForceDeleteHandlerTrait;
57
    use RestoreHandlerTrait;
58
    use UpdateHandlerTrait;
59
    use StoreHandlerTrait;
60
    use ListHandlerTrait;
61
    use EditHandlerTrait;
62
    use CreateHandlerTrait;
63
    use OrderByHandlerTrait;
64
    use PerPageHandlerTrait;
65
    use SortableHandlerTrait;
66
    use SearchRelationHandlerTrait;
67
    use SearchHandlerTrait;
68
69
    protected $viewCrudList = 'jarboe::crud.list';
70
    protected $viewCrudCreate = 'jarboe::crud.create';
71
    protected $viewCrudEdit = 'jarboe::crud.edit';
72
73
    /**
74
     * Permission group name.
75
     *
76
     * @var string|array
77
     * array(
78
     *     'list'        => 'permission:list',
79
     *     'search'      => 'permission:search',
80
     *     'create'      => 'permission:create',
81
     *     'store'       => 'permission:store',
82
     *     'edit'        => 'permission:edit',
83
     *     'update'      => 'permission:update',
84
     *     'inline'      => 'permission:inline',
85
     *     'delete'      => 'permission:delete',
86
     *     'restore'     => 'permission:restore',
87
     *     'forceDelete' => 'permission:force-delete',
88
     * )
89
     */
90
    protected $permissions = '';
91
92
    /**
93
     * @var CRUD
94
     */
95
    protected $crud;
96
97
    /**
98
     * ID of manipulated model.
99
     *
100
     * @var mixed
101
     */
102
    protected $idEntity;
103
104 17
    public function __construct()
105
    {
106 17
        $this->crud = app(CRUD::class);
107 17
        $this->crud()->tableIdentifier(crc32(static::class));
108 17
        $this->crud()->formClass(config('jarboe.crud.form_class'));
109 17
        $this->crud()->actions()->set([
110 17
            CreateAction::make(),
111 17
            EditAction::make(),
112 17
            RestoreAction::make(),
113 17
            DeleteAction::make(),
114 17
            ForceDeleteAction::make(),
115
        ]);
116 17
    }
117
118
    protected function crud(): CRUD
119
    {
120
        return $this->crud;
121
    }
122
123
    /**
124
     * Check if user has permission for the action.
125
     *
126
     * @param $action
127
     * @return bool
128
     */
129 11
    protected function can($action): bool
130
    {
131 11
        if (!$this->permissions) {
132 11
            return true;
133
        }
134
        if (is_array($this->permissions) && !array_key_exists($action, $this->permissions)) {
135
            return true;
136
        }
137
138
        if (is_array($this->permissions)) {
139
            $permission = $this->permissions[$action];
140
        } else {
141
            $permission = sprintf('%s:%s', $this->permissions, $action);
142
        }
143
144
        return $this->admin()->can($permission);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Auth\Authenticatable as the method can() does only exist in the following implementations of said interface: Illuminate\Foundation\Auth\User, Yaro\Jarboe\Models\Admin.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
145
    }
146
147 10
    public function __call($name, $arguments)
148
    {
149
        /** @var Request $request */
150 10
        $request = RequestFacade::instance();
151
152 10
        $id = null;
153 10
        if (isset($arguments[0])) {
154 9
            $id = $arguments[1] ?? $arguments[0];
155
        }
156
157
        try {
158 10
            switch ($name) {
159 10
                case 'list':
160 1
                    return $this->handleList($request);
161 9
                case 'search':
162 1
                    return $this->handleSearch($request);
163 8
                case 'create':
164 1
                    return $this->handleCreate($request);
165 7
                case 'store':
166 1
                    return $this->handleStore($request);
167 6
                case 'edit':
168 1
                    return $this->handleEdit($request, $id);
169 5
                case 'update':
170 1
                    return $this->handleUpdate($request, $id);
171 4
                case 'delete':
172 1
                    return $this->handleDelete($request, $id);
173 3
                case 'restore':
174 1
                    return $this->handleRestore($request, $id);
175 2
                case 'forceDelete':
176 1
                    return $this->handleForceDelete($request, $id);
177 1
                case 'inline':
178 1
                    return $this->handleInline($request);
179
180
                default:
181
                    throw new \RuntimeException('Invalid method ' . $name);
182
            }
183
        } catch (ValidationException $e) {
184
            throw $e;
185
        } catch (UnauthorizedException $e) {
186
            return $this->createUnauthorizedResponse($request, $e);
187
        } catch (\Exception $e) {
188
            $this->notifyBigDanger(get_class($e), $e->getMessage(), 0);
189
            return redirect()->back()->withInput($request->input());
190
        }
191
    }
192
193
    /**
194
     * Bound fields/tools/etc with global data.
195
     */
196 13
    protected function bound()
197
    {
198
        /** @var AbstractField $field */
199 13
        foreach ($this->crud()->getAllFieldObjects() as $field) {
200 13
            $field->prepare($this->crud);
201
        }
202
203
        /** @var AbstractField $field */
204 13
        foreach ($this->crud()->getFieldsWithoutMarkup() as $field) {
205 13
            if ($field->isTranslatable()) {
206
                $this->addTool(new TranslationLocalesSelectorTool());
207
                break;
208
            }
209
        }
210
211 13
        $this->crud()->actions()->setCrud($this->crud());
212 13
    }
213
214
    /**
215
     * Create response for unauthorized request.
216
     *
217
     * @param Request $request
218
     * @param UnauthorizedException $exception
219
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\JsonResponse|\Illuminate\View\View
220
     */
221
    protected function createUnauthorizedResponse(Request $request, UnauthorizedException $exception)
222
    {
223
        if ($request->wantsJson()) {
224
            return response()->json([
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
225
                'message' => $exception->getMessage()
226
            ], 401);
227
        }
228
229
        return view('jarboe::errors.401');
230
    }
231
232
    /**
233
     * Get model for current request.
234
     *
235
     * @return string
236
     * @throws \RuntimeException
237
     */
238
    protected function model()
239
    {
240
        if (!$this->idEntity) {
241
            throw new \RuntimeException('Trying to access to non-existed entity');
242
        }
243
244
        return $this->crud()->repo()->find($this->idEntity);
245
    }
246
247
    /**
248
     * @return void
249
     */
250
    abstract protected function init();
251
}
252