Completed
Push — master ( f066c0...72d72a )
by Yaro
01:50 queued 10s
created

AbstractTableController::handleInline()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 11.6408

Importance

Changes 0
Metric Value
dl 0
loc 44
rs 7.9715
c 0
b 0
f 0
ccs 16
cts 26
cp 0.6153
cc 8
nc 8
nop 1
crap 11.6408

2 Methods

Rating   Name   Duplication   Size   Complexity  
A AbstractTableController::createUnauthorizedResponse() 0 10 2
A AbstractTableController::model() 0 8 2
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
            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 13
                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
    abstract protected function init();
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
248
}
249