Completed
Push — master ( afd3db...d9ce46 )
by Yaro
06:54
created

AbstractTableController::__call()   F

Complexity

Conditions 20
Paths 166

Size

Total Lines 68

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 21.6109

Importance

Changes 0
Metric Value
dl 0
loc 68
ccs 37
cts 44
cp 0.8409
rs 3.6166
c 0
b 0
f 0
cc 20
nc 166
nop 2
crap 21.6109

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\RedirectResponse;
7
use Illuminate\Http\Request;
8
use Illuminate\Support\Facades\Request as RequestFacade;
9
use Illuminate\Validation\ValidationException;
10
use Spatie\Permission\Exceptions\UnauthorizedException;
11
use Yaro\Jarboe\Http\Controllers\Traits\AliasesTrait;
12
use Yaro\Jarboe\Http\Controllers\Traits\BreadcrumbsTrait;
13
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\CreateHandlerTrait;
14
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\DeleteHandlerTrait;
15
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\EditHandlerTrait;
16
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\ForceDeleteHandlerTrait;
17
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\InlineHandlerTrait;
18
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\ListHandlerTrait;
19
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\OrderByHandlerTrait;
20
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\PerPageHandlerTrait;
21
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\RenderRepeaterItemHandlerTrait;
22
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\RestoreHandlerTrait;
23
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\SearchHandlerTrait;
24
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\SearchRelationHandlerTrait;
25
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\SortableHandlerTrait;
26
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\StoreHandlerTrait;
27
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\ToolbarHandlerTrait;
28
use Yaro\Jarboe\Http\Controllers\Traits\Handlers\UpdateHandlerTrait;
29
use Yaro\Jarboe\Http\Controllers\Traits\NotifyTrait;
30
use Yaro\Jarboe\Table\Actions\CreateAction;
31
use Yaro\Jarboe\Table\Actions\DeleteAction;
32
use Yaro\Jarboe\Table\Actions\EditAction;
33
use Yaro\Jarboe\Table\Actions\ForceDeleteAction;
34
use Yaro\Jarboe\Table\Actions\RestoreAction;
35
use Yaro\Jarboe\Table\CRUD;
36
use Yaro\Jarboe\Table\Fields\AbstractField;
37
use Yaro\Jarboe\Table\Toolbar\TranslationLocalesSelectorTool;
38
use Yaro\Jarboe\ViewComponents\Breadcrumbs\BreadcrumbsInterface;
39
40
/**
41
 * @method mixed list(Request $request)
42
 * @method mixed search(Request $request)
43
 * @method mixed create(Request $request)
44
 * @method mixed store(Request $request)
45
 * @method mixed edit(Request $request, $id)
46
 * @method mixed update(Request $request, $id)
47
 * @method mixed delete(Request $request, $id)
48
 * @method mixed restore(Request $request, $id)
49
 * @method mixed forceDelete(Request $request, $id)
50
 * @method mixed inline(Request $request)
51
 */
52
abstract class AbstractTableController
53
{
54
    use ValidatesRequests;
55
    use NotifyTrait;
56
    use AliasesTrait;
57
    use DeleteHandlerTrait;
58
    use ToolbarHandlerTrait;
59
    use InlineHandlerTrait;
60
    use ForceDeleteHandlerTrait;
61
    use RestoreHandlerTrait;
62
    use UpdateHandlerTrait;
63
    use StoreHandlerTrait;
64
    use ListHandlerTrait;
65
    use EditHandlerTrait;
66
    use CreateHandlerTrait;
67
    use OrderByHandlerTrait;
68
    use PerPageHandlerTrait;
69
    use SortableHandlerTrait;
70
    use SearchRelationHandlerTrait;
71
    use SearchHandlerTrait;
72
    use BreadcrumbsTrait;
73
    use RenderRepeaterItemHandlerTrait;
74
75
    /**
76
     * Permission group name.
77
     *
78
     * @var string|array
79
     * array(
80
     *     'list'        => 'permission:list',
81
     *     'search'      => 'permission:search',
82
     *     'create'      => 'permission:create',
83
     *     'store'       => 'permission:store',
84
     *     'edit'        => 'permission:edit',
85
     *     'update'      => 'permission:update',
86
     *     'inline'      => 'permission:inline',
87
     *     'delete'      => 'permission:delete',
88
     *     'restore'     => 'permission:restore',
89
     *     'forceDelete' => 'permission:force-delete',
90
     * )
91
     */
92
    protected $permissions = '';
93
94
    /**
95
     * @var CRUD
96
     */
97
    protected $crud;
98
99
    /**
100
     * ID of manipulated model.
101
     *
102
     * @var mixed
103
     */
104
    protected $idEntity;
105 71
106
    public function __construct()
107 71
    {
108 71
        $this->crud = app(CRUD::class);
109 71
        $this->crud()->tableIdentifier(crc32(static::class));
110 71
        $this->crud()->formClass(config('jarboe.crud.form_class'));
111 71
        $this->crud()->actions()->set([
112 71
            CreateAction::make(),
113 71
            EditAction::make(),
114 71
            RestoreAction::make(),
115 71
            DeleteAction::make(),
116
            ForceDeleteAction::make(),
117 71
        ]);
118 71
        $this->breadcrumbs = app(BreadcrumbsInterface::class);
119
    }
120 71
121
    protected function crud(): CRUD
122 71
    {
123
        return $this->crud;
124
    }
125
126
    /**
127
     * Check if user has permission for the action.
128
     *
129
     * @param $action
130
     * @return bool
131 36
     */
132
    protected function can($action): bool
133 36
    {
134 16
        if (!$this->permissions) {
135
            return true;
136 21
        }
137 1
        if (is_array($this->permissions) && !array_key_exists($action, $this->permissions)) {
138
            return true;
139
        }
140 21
141 1
        if (is_array($this->permissions)) {
142
            $permission = $this->permissions[$action];
143 21
        } else {
144
            $permission = sprintf('%s:%s', $this->permissions, $action);
145
        }
146 21
147
        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...
148
    }
149 24
150
    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...
151
    {
152 24
        /** @var Request $request */
153
        $request = RequestFacade::instance();
154 24
155 24
        $id = null;
156 23
        if (isset($arguments[0])) {
157
            $id = $arguments[1] ?? $arguments[0];
158
        }
159
160 24
        try {
161 24
            switch ($name) {
162 4
                case 'list':
163 20
                    return $this->handleList($request);
164 2
                case 'search':
165 18
                    return $this->handleSearch($request);
166 2
                case 'create':
167 16
                    return $this->handleCreate($request);
168 2
                case 'store':
169 14
                    return $this->handleStore($request);
170 2
                case 'edit':
171 12
                    return $this->handleEdit($request, $id);
172 2
                case 'update':
173 10
                    return $this->handleUpdate($request, $id);
174 2
                case 'delete':
175 8
                    return $this->handleDelete($request, $id);
176 3
                case 'restore':
177 5
                    return $this->handleRestore($request, $id);
178 2
                case 'forceDelete':
179 3
                    return $this->handleForceDelete($request, $id);
180 2
                case 'inline':
181 1
                    return $this->handleInline($request);
182
                case 'renderRepeaterItem':
183
                    $fieldName = $id;
184
                    return $this->handleRenderRepeaterItem($request, $fieldName);
185
186 1
                default:
187
                    throw new \RuntimeException('Invalid method ' . $name);
188 12
            }
189 1
        } catch (ValidationException $e) {
190 11
            throw $e;
191 10
        } catch (UnauthorizedException $e) {
192 1
            return $this->createUnauthorizedResponse($request, $e);
193 1
        } catch (\Throwable $e) {
194 1
            $response = $this->onException($e);
195
            if (!is_null($response)) {
196
                return $response;
197
            }
198
199 1
            // TODO: response objects
200
            if ($request->isXmlHttpRequest() || $request->wantsJson()) {
201
                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...
202
                    'title' => get_class($e),
203
                    'description' => $e->getMessage(),
204
                ], 406);
205
            }
206 1
207 1
            $this->notifyBigDanger(get_class($e), $e->getMessage(), 0);
208
209
            /** @var RedirectResponse $redirect */
210
            $redirect = redirect()->back();
211
            if ($redirect->getTargetUrl() == $request->url()) {
212
                $redirect->setTargetUrl(admin_url());
213
            }
214
215
            return $redirect->withInput($request->input());
216
        }
217
    }
218
219 1
    /**
220
     * Event for processing exceptions before forming error response.
221
     * Non-null return value will be processed as response.
222 1
     *
223
     * @param \Throwable $exception
224
     *
225
     * @return mixed|void
226
     */
227
    protected function onException(\Throwable $exception)
228
    {
229 43
        // dummy
230
    }
231
232 43
    /**
233
     * Method for overriding to define basic/common init() settings.
234
     *
235
     * @return void
236
     */
237 51
    protected function beforeInit()
238
    {
239
        // dummy
240 51
    }
241 50
242
    /**
243
     * Bound fields/tools/etc with global data.
244
     */
245 51
    protected function bound()
246 50
    {
247
        /** @var AbstractField $field */
248
        foreach ($this->crud()->getAllFieldObjects() as $field) {
249
            $field->prepare($this->crud);
250
        }
251
252 51
        /** @var AbstractField $field */
253 51
        foreach ($this->crud()->getFieldsWithoutMarkup() as $field) {
254 51
            if ($field->isTranslatable()) {
255
                $this->addTool(new TranslationLocalesSelectorTool());
256
                break;
257
            }
258
        }
259
260
        $this->crud()->actions()->setCrud($this->crud());
261
        $this->initBreadcrumbs();
262
    }
263 11
264
    /**
265 11
     * Create response for unauthorized request.
266 1
     *
267 1
     * @param Request $request
268 1
     * @param UnauthorizedException $exception
269
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\JsonResponse|\Illuminate\View\View
270
     */
271 11
    protected function createUnauthorizedResponse(Request $request, UnauthorizedException $exception)
272
    {
273
        if ($request->wantsJson()) {
274
            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...
275
                'message' => $exception->getMessage()
276
            ], 401);
277
        }
278
279
        return view('jarboe::errors.401');
280
    }
281
282
    /**
283
     * Get model for current request.
284
     *
285
     * @return string
286
     * @throws \RuntimeException
287
     */
288
    protected function model()
289
    {
290
        if (!$this->idEntity) {
291
            throw new \RuntimeException('Trying to access to non-existed entity');
292
        }
293
294
        return $this->crud()->repo()->find($this->idEntity);
295
    }
296
297
    /**
298
     * @return void
299
     */
300
    abstract protected function init();
301
}
302