Completed
Pull Request — master (#15)
by Yaro
08:09 queued 01:44
created

AbstractTableController::__call()   D

Complexity

Conditions 18
Paths 118

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 18.3499

Importance

Changes 0
Metric Value
dl 0
loc 56
rs 4.7166
c 0
b 0
f 0
ccs 35
cts 39
cp 0.8974
cc 18
nc 118
nop 2
crap 18.3499

How to fix   Long Method    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\BreadcrumbsTrait;
12
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\CreateHandlerTrait;
13
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\DeleteHandlerTrait;
14
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\EditHandlerTrait;
15
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\ForceDeleteHandlerTrait;
16
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\InlineHandlerTrait;
17
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\ListHandlerTrait;
18
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\OrderByHandlerTrait;
19
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\PerPageHandlerTrait;
20
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\RenderRepeaterItemHandlerTrait;
21
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\RestoreHandlerTrait;
22
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\SearchHandlerTrait;
23
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\SearchRelationHandlerTrait;
24
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\SortableHandlerTrait;
25
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\StoreHandlerTrait;
26
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\ToolbarHandlerTrait;
27
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\UpdateHandlerTrait;
28
use Yaro\Jarboe\Http\Controllers\Traits\NotifyTrait;
29
use Yaro\Jarboe\Table\Actions\CreateAction;
30
use Yaro\Jarboe\Table\Actions\DeleteAction;
31
use Yaro\Jarboe\Table\Actions\EditAction;
32
use Yaro\Jarboe\Table\Actions\ForceDeleteAction;
33
use Yaro\Jarboe\Table\Actions\RestoreAction;
34
use Yaro\Jarboe\Table\CRUD;
35
use Yaro\Jarboe\Table\Fields\AbstractField;
36
use Yaro\Jarboe\Table\Toolbar\TranslationLocalesSelectorTool;
37
use Yaro\Jarboe\ViewComponents\Breadcrumbs\BreadcrumbsInterface;
38
39
/**
40
 * @method mixed list(Request $request)
41
 * @method mixed search(Request $request)
42
 * @method mixed create(Request $request)
43
 * @method mixed store(Request $request)
44
 * @method mixed edit(Request $request, $id)
45
 * @method mixed update(Request $request, $id)
46
 * @method mixed delete(Request $request, $id)
47
 * @method mixed restore(Request $request, $id)
48
 * @method mixed forceDelete(Request $request, $id)
49
 * @method mixed inline(Request $request)
50
 */
51
abstract class AbstractTableController
52
{
53
    use ValidatesRequests;
54
    use NotifyTrait;
55
    use AliasesTrait;
56
    use DeleteHandlerTrait;
57
    use ToolbarHandlerTrait;
58
    use InlineHandlerTrait;
59
    use ForceDeleteHandlerTrait;
60
    use RestoreHandlerTrait;
61
    use UpdateHandlerTrait;
62
    use StoreHandlerTrait;
63
    use ListHandlerTrait;
64
    use EditHandlerTrait;
65
    use CreateHandlerTrait;
66
    use OrderByHandlerTrait;
67
    use PerPageHandlerTrait;
68
    use SortableHandlerTrait;
69
    use SearchRelationHandlerTrait;
70
    use SearchHandlerTrait;
71
    use BreadcrumbsTrait;
72
    use RenderRepeaterItemHandlerTrait;
73
74
    /**
75
     * Permission group name.
76
     *
77
     * @var string|array
78
     * array(
79
     *     'list'        => 'permission:list',
80
     *     'search'      => 'permission:search',
81
     *     'create'      => 'permission:create',
82
     *     'store'       => 'permission:store',
83
     *     'edit'        => 'permission:edit',
84
     *     'update'      => 'permission:update',
85
     *     'inline'      => 'permission:inline',
86
     *     'delete'      => 'permission:delete',
87
     *     'restore'     => 'permission:restore',
88
     *     'forceDelete' => 'permission:force-delete',
89
     * )
90
     */
91
    protected $permissions = '';
92
93
    /**
94
     * @var CRUD
95
     */
96
    protected $crud;
97
98
    /**
99
     * ID of manipulated model.
100
     *
101
     * @var mixed
102
     */
103 70
    protected $idEntity;
104
105 70
    public function __construct()
106 70
    {
107 70
        $this->crud = app(CRUD::class);
108 70
        $this->crud()->tableIdentifier(crc32(static::class));
109 70
        $this->crud()->formClass(config('jarboe.crud.form_class'));
110 70
        $this->crud()->actions()->set([
111 70
            CreateAction::make(),
112 70
            EditAction::make(),
113 70
            RestoreAction::make(),
114
            DeleteAction::make(),
115 70
            ForceDeleteAction::make(),
116 70
        ]);
117
        $this->breadcrumbs = app(BreadcrumbsInterface::class);
118 70
    }
119
120 70
    protected function crud(): CRUD
121
    {
122
        return $this->crud;
123
    }
124
125
    /**
126
     * Check if user has permission for the action.
127
     *
128
     * @param $action
129 35
     * @return bool
130
     */
131 35
    protected function can($action): bool
132 15
    {
133
        if (!$this->permissions) {
134 21
            return true;
135 1
        }
136
        if (is_array($this->permissions) && !array_key_exists($action, $this->permissions)) {
137
            return true;
138 21
        }
139 1
140
        if (is_array($this->permissions)) {
141 21
            $permission = $this->permissions[$action];
142
        } else {
143
            $permission = sprintf('%s:%s', $this->permissions, $action);
144 21
        }
145
146
        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...
147 23
    }
148
149
    public function __call($name, $arguments)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
150 23
    {
151
        /** @var Request $request */
152 23
        $request = RequestFacade::instance();
153 23
154 22
        $id = null;
155
        if (isset($arguments[0])) {
156
            $id = $arguments[1] ?? $arguments[0];
157
        }
158 23
159 23
        try {
160 3
            switch ($name) {
161 20
                case 'list':
162 2
                    return $this->handleList($request);
163 18
                case 'search':
164 2
                    return $this->handleSearch($request);
165 16
                case 'create':
166 2
                    return $this->handleCreate($request);
167 14
                case 'store':
168 2
                    return $this->handleStore($request);
169 12
                case 'edit':
170 2
                    return $this->handleEdit($request, $id);
171 10
                case 'update':
172 2
                    return $this->handleUpdate($request, $id);
173 8
                case 'delete':
174 3
                    return $this->handleDelete($request, $id);
175 5
                case 'restore':
176 2
                    return $this->handleRestore($request, $id);
177 3
                case 'forceDelete':
178 2
                    return $this->handleForceDelete($request, $id);
179
                case 'inline':
180
                    return $this->handleInline($request);
181 1
                case 'renderRepeaterItem':
182
                    $fieldName = $id;
183 12
                    return $this->handleRenderRepeaterItem($request, $fieldName);
184 1
185 11
                default:
186 10
                    throw new \RuntimeException('Invalid method ' . $name);
187 1
            }
188
        } catch (ValidationException $e) {
189 1
            throw $e;
190
        } catch (UnauthorizedException $e) {
191
            return $this->createUnauthorizedResponse($request, $e);
192
        } catch (\Throwable $e) {
193
            // TODO: response objects
194
            if ($request->isXmlHttpRequest() || $request->wantsJson()) {
195
                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...
196 1
                    'title' => get_class($e),
197 1
                    'description' => $e->getMessage(),
198
                ], 406);
199
            }
200
201
            $this->notifyBigDanger(get_class($e), $e->getMessage(), 0);
202
            return redirect()->back()->withInput($request->input());
203
        }
204 50
    }
205
206
    /**
207 50
     * Bound fields/tools/etc with global data.
208 49
     */
209
    protected function bound()
210
    {
211
        /** @var AbstractField $field */
212 50
        foreach ($this->crud()->getAllFieldObjects() as $field) {
213 49
            $field->prepare($this->crud);
214
        }
215
216
        /** @var AbstractField $field */
217
        foreach ($this->crud()->getFieldsWithoutMarkup() as $field) {
218
            if ($field->isTranslatable()) {
219 50
                $this->addTool(new TranslationLocalesSelectorTool());
220 50
                break;
221 50
            }
222
        }
223
224
        $this->crud()->actions()->setCrud($this->crud());
225
        $this->initBreadcrumbs();
226
    }
227
228
    /**
229
     * Create response for unauthorized request.
230 11
     *
231
     * @param Request $request
232 11
     * @param UnauthorizedException $exception
233 1
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\JsonResponse|\Illuminate\View\View
234 1
     */
235 1
    protected function createUnauthorizedResponse(Request $request, UnauthorizedException $exception)
236
    {
237
        if ($request->wantsJson()) {
238 11
            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...
239
                'message' => $exception->getMessage()
240
            ], 401);
241
        }
242
243
        return view('jarboe::errors.401');
244
    }
245
246
    /**
247
     * Get model for current request.
248
     *
249
     * @return string
250
     * @throws \RuntimeException
251
     */
252
    protected function model()
253
    {
254
        if (!$this->idEntity) {
255
            throw new \RuntimeException('Trying to access to non-existed entity');
256
        }
257
258
        return $this->crud()->repo()->find($this->idEntity);
259
    }
260
261
    /**
262
     * @return void
263
     */
264
    abstract protected function init();
265
}
266