Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Push — main ( 3714d7...b58f97 )
by Pedro
44:24 queued 29:28
created

SaveActions   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 412
Duplicated Lines 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
eloc 138
c 3
b 2
f 0
dl 0
loc 412
rs 4.5599
wmc 58

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getSaveActionDefaultForCurrentOperation() 0 3 1
A getSaveActionByOrder() 0 4 1
A addSaveActions() 0 8 3
A setSaveAction() 0 15 4
A getFallBackSaveAction() 0 15 4
A setSaveActions() 0 3 1
A removeSaveAction() 0 7 2
A orderSaveAction() 0 18 6
A getVisibleSaveActions() 0 13 3
A removeAllSaveActions() 0 3 1
A getOrderedSaveActions() 0 9 1
A orderSaveActions() 0 7 3
A replaceSaveActions() 0 9 2
A getCurrentSaveAction() 0 13 2
A removeSaveActions() 0 4 2
A addSaveAction() 0 17 5
A getSaveAction() 0 23 4
A setupDefaultSaveActions() 0 48 5
B performSaveAction() 0 36 8

How to fix   Complexity   

Complex Class

Complex classes like SaveActions 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.

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 SaveActions, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel\Traits;
4
5
use Illuminate\Support\Arr;
6
7
trait SaveActions
8
{
9
    /**
10
     * Get the developer's preference on what save action is the default one
11
     * for the current operation.
12
     *
13
     * @return string
14
     */
15
    public function getSaveActionDefaultForCurrentOperation()
16
    {
17
        return config('backpack.crud.operations.'.$this->getCurrentOperation().'.defaultSaveAction', 'save_and_back');
0 ignored issues
show
Bug introduced by
The method getCurrentOperation() does not exist on Backpack\CRUD\app\Librar...anel\Traits\SaveActions. Did you maybe mean getCurrentSaveAction()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

17
        return config('backpack.crud.operations.'.$this->/** @scrutinizer ignore-call */ getCurrentOperation().'.defaultSaveAction', 'save_and_back');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
18
    }
19
20
    /**
21
     * Get the save action with full fallback until default.
22
     *
23
     * @return string
24
     */
25
    public function getFallBackSaveAction()
26
    {
27
        //we get the higher order in save actions array. By default it would be `save_and_back`
28
        $higherAction = $this->getSaveActionByOrder(1);
29
30
        //if there is an higher action and that action is not the backpack default higher one `save_and_back` we return it.
31
        if (! empty($higherAction) && key($higherAction) !== 'save_and_back') {
32
            return key($higherAction);
33
        }
34
35
        if ($this->hasOperationSetting('defaultSaveAction')) {
0 ignored issues
show
Bug introduced by
It seems like hasOperationSetting() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

35
        if ($this->/** @scrutinizer ignore-call */ hasOperationSetting('defaultSaveAction')) {
Loading history...
36
            return $this->getOperationSetting('defaultSaveAction');
0 ignored issues
show
Bug introduced by
It seems like getOperationSetting() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

36
            return $this->/** @scrutinizer ignore-call */ getOperationSetting('defaultSaveAction');
Loading history...
37
        }
38
39
        return $this->getSaveActionDefaultForCurrentOperation();
40
    }
41
42
    /**
43
     * Gets the save action that has the desired order.
44
     *
45
     * @param  int  $order
46
     * @return array
47
     */
48
    public function getSaveActionByOrder($order)
49
    {
50
        return array_filter($this->getOperationSetting('save_actions') ?? [], function ($arr) use ($order) {
51
            return $arr['order'] == $order;
52
        });
53
    }
54
55
    /**
56
     * Allow the developer to register multiple save actions.
57
     *
58
     * @param  array  $saveActions
59
     * @return void
60
     */
61
    public function addSaveActions($saveActions)
62
    {
63
        // count vs count recursive will be different when counting single dimension vs multiple dimension arrays.
64
        // count([1,2]) = 2, count([1,[2,3]]) = 2 with recursive it's 3. so if counts are different we have a
65
        // multi dimensional array
66
        if (count($saveActions) != count($saveActions, COUNT_RECURSIVE)) {
67
            foreach ($saveActions as $saveAction) {
68
                $this->addSaveAction($saveAction);
69
            }
70
        }
71
    }
72
73
    /**
74
     * Allow developers to register save action into CRUD.
75
     *
76
     * @param  array  $saveAction
77
     * @return void
78
     */
79
    public function addSaveAction(array $saveAction)
80
    {
81
        $orderCounter = $this->getOperationSetting('save_actions') !== null ? (count($this->getOperationSetting('save_actions')) + 1) : 1;
82
        //check for some mandatory fields
83
        $saveAction['name'] ?? abort(500, 'Please define save action name.');
0 ignored issues
show
Bug introduced by
Are you sure the usage of abort(500, 'Please define save action name.') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
84
        $saveAction['redirect'] = $saveAction['redirect'] ?? fn ($crud, $request, $itemId) => $request->has('_http_referrer') ? $request->get('_http_referrer') : $crud->route;
0 ignored issues
show
Unused Code introduced by
The parameter $itemId is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

84
        $saveAction['redirect'] = $saveAction['redirect'] ?? fn ($crud, $request, /** @scrutinizer ignore-unused */ $itemId) => $request->has('_http_referrer') ? $request->get('_http_referrer') : $crud->route;

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
85
        $saveAction['visible'] = $saveAction['visible'] ?? true;
86
        $saveAction['button_text'] = $saveAction['button_text'] ?? $saveAction['name'];
87
        $saveAction['order'] = isset($saveAction['order']) ? $this->orderSaveAction($saveAction['name'], $saveAction['order']) : $orderCounter;
88
89
        $actions = $this->getOperationSetting('save_actions') ?? [];
90
91
        if (! in_array($saveAction['name'], $actions)) {
92
            $actions[$saveAction['name']] = $saveAction;
93
        }
94
95
        $this->setOperationSetting('save_actions', $actions);
0 ignored issues
show
Bug introduced by
It seems like setOperationSetting() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

95
        $this->/** @scrutinizer ignore-call */ 
96
               setOperationSetting('save_actions', $actions);
Loading history...
96
    }
97
98
    /**
99
     * Replaces setting order or forces some default.
100
     *
101
     * @param  string  $saveAction
102
     * @param  int  $wantedOrder
103
     * @return int
104
     */
105
    public function orderSaveAction(string $saveAction, int $wantedOrder)
106
    {
107
        $actions = $this->getOperationSetting('save_actions') ?? [];
108
        if (! empty($actions)) {
109
            $replaceOrder = isset($actions[$saveAction]) ? $actions[$saveAction]['order'] : count($actions) + 1;
110
111
            foreach ($actions as $key => $sv) {
112
                if ($wantedOrder == $sv['order']) {
113
                    $actions[$key]['order'] = $replaceOrder;
114
                }
115
                if ($key == $saveAction) {
116
                    $actions[$key]['order'] = $wantedOrder;
117
                }
118
            }
119
            $this->setOperationSetting('save_actions', $actions);
120
        }
121
122
        return $wantedOrder;
123
    }
124
125
    /**
126
     * Replace the current save actions with the ones provided.
127
     *
128
     * @param  array  $saveActions
129
     * @return void
130
     */
131
    public function replaceSaveActions($saveActions)
132
    {
133
        //we reset all save actions
134
        $this->setOperationSetting('save_actions', []);
135
136
        if (count($saveActions) != count($saveActions, COUNT_RECURSIVE)) {
137
            $this->addSaveActions($saveActions);
138
        } else {
139
            $this->addSaveAction($saveActions);
140
        }
141
    }
142
143
    /**
144
     * Alias function of replaceSaveActions() for CRUD consistency.
145
     *
146
     * @param  array  $saveActions
147
     * @return void
148
     */
149
    public function setSaveActions($saveActions)
150
    {
151
        return $this->replaceSaveActions($saveActions);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->replaceSaveActions($saveActions) targeting Backpack\CRUD\app\Librar...s::replaceSaveActions() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
152
    }
153
154
    /**
155
     * Allow the developer to remove multiple save actions from settings.
156
     *
157
     * @param  array  $saveActions
158
     * @return void
159
     */
160
    public function removeSaveActions(array $saveActions)
161
    {
162
        foreach ($saveActions as $sv) {
163
            $this->removeSaveAction($sv);
164
        }
165
    }
166
167
    /**
168
     * Allow the developer to remove a save action from settings.
169
     *
170
     * @param  string  $saveAction
171
     * @return void
172
     */
173
    public function removeSaveAction(string $saveAction)
174
    {
175
        $actions = $this->getOperationSetting('save_actions') ?? [];
176
        if (isset($actions[$saveAction])) {
177
            $actions[$saveAction] = null;
178
        }
179
        $this->setOperationSetting('save_actions', array_filter($actions));
180
    }
181
182
    /**
183
     * Allow the developer to unset all save actions.
184
     *
185
     * @param  string  $saveAction
186
     * @return void
187
     */
188
    public function removeAllSaveActions()
189
    {
190
        $this->setOperationSetting('save_actions', []);
191
    }
192
193
    /**
194
     * Allows the developer to set save actions order. It could be ['action1','action2'] or ['action1' => 1, 'action2' => 2].
195
     *
196
     * @param  array  $saveActions
197
     * @return void
198
     */
199
    public function orderSaveActions(array $saveActions)
200
    {
201
        foreach ($saveActions as $sv => $order) {
202
            if (! is_int($order)) {
203
                $this->orderSaveAction($order, $sv + 1);
204
            } else {
205
                $this->orderSaveAction($sv, $order);
206
            }
207
        }
208
    }
209
210
    /**
211
     * Return the ordered save actions to use in the crud panel.
212
     *
213
     * @return array
214
     */
215
    public function getOrderedSaveActions()
216
    {
217
        $actions = $this->getOperationSetting('save_actions') ?? [];
218
219
        uasort($actions, function ($a, $b) {
220
            return $a['order'] <=> $b['order'];
221
        });
222
223
        return $actions;
224
    }
225
226
    /**
227
     * Returns the save actions that passed the visible callback.
228
     *
229
     * @return array
230
     */
231
    public function getVisibleSaveActions()
232
    {
233
        $actions = $this->getOrderedSaveActions();
234
        foreach ($actions as $actionName => $action) {
235
            $visible = $action['visible'];
236
            if ($visible instanceof \Closure) {
237
                $actions[$actionName]['visible'] = $visible($this);
238
            }
239
        }
240
241
        return array_filter($actions, function ($action) {
242
            return $action['visible'] == true;
243
        }, ARRAY_FILTER_USE_BOTH);
244
    }
245
246
    /**
247
     * Gets the current save action for this crud.
248
     *
249
     * @param  array  $saveOptions
250
     * @return array
251
     */
252
    public function getCurrentSaveAction($saveOptions)
253
    {
254
        //get save action from session if exists, or get the developer defined order
255
        $saveAction = session($this->getCurrentOperation().'.saveAction', $this->getFallBackSaveAction());
256
        if (isset($saveOptions[$saveAction])) {
257
            $currentAction = $saveOptions[$saveAction];
258
        } else {
259
            $currentAction = Arr::first($saveOptions);
260
        }
261
262
        return [
263
            'value' => $currentAction['name'],
264
            'label' => $currentAction['button_text'],
265
        ];
266
    }
267
268
    /**
269
     * Here we check for save action visibility and prepare the actions array for display.
270
     *
271
     * @return array
272
     */
273
    public function getSaveAction()
274
    {
275
        //get only the save actions that pass visibility callback
276
        $saveOptions = $this->getVisibleSaveActions();
277
278
        if (empty($saveOptions)) {
279
            return [];
280
        }
281
282
        //get the current action
283
        $saveCurrent = $this->getCurrentSaveAction($saveOptions);
284
285
        //get the dropdown options
286
        $dropdownOptions = [];
287
        foreach ($saveOptions as $key => $option) {
288
            if ($option['name'] != $saveCurrent['value']) {
289
                $dropdownOptions[$option['name']] = $option['button_text'];
290
            }
291
        }
292
293
        return [
294
            'active' => $saveCurrent,
295
            'options' => $dropdownOptions,
296
        ];
297
    }
298
299
    /**
300
     * Change the session variable that remembers what to do after the "Save" action.
301
     *
302
     * @param  string|null  $forceSaveAction
303
     * @return void
304
     */
305
    public function setSaveAction($forceSaveAction = null)
306
    {
307
        $saveAction = $forceSaveAction ?:
308
            $this->getRequest()->input('_save_action', $this->getFallBackSaveAction());
0 ignored issues
show
Bug introduced by
It seems like getRequest() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

308
            $this->/** @scrutinizer ignore-call */ 
309
                   getRequest()->input('_save_action', $this->getFallBackSaveAction());
Loading history...
309
310
        $showBubble = $this->getOperationSetting('showSaveActionChange') ?? config('backpack.crud.operations.'.$this->getCurrentOperation().'.showSaveActionChange') ?? true;
311
312
        if (
313
            $showBubble &&
314
            session($this->getCurrentOperation().'.saveAction', 'save_and_back') !== $saveAction
315
        ) {
316
            \Alert::info(trans('backpack::crud.save_action_changed_notification'))->flash();
0 ignored issues
show
Bug introduced by
The type Alert was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
317
        }
318
319
        session([$this->getCurrentOperation().'.saveAction' => $saveAction]);
320
    }
321
322
    /**
323
     * Redirect to the correct URL, depending on which save action has been selected.
324
     *
325
     * @param  string  $itemId
326
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
327
     */
328
    public function performSaveAction($itemId = null)
329
    {
330
        $request = $this->getRequest();
331
        $saveAction = $request->input('_save_action', $this->getFallBackSaveAction());
332
        $itemId = $itemId ?: $request->input('id');
333
        $actions = $this->getOperationSetting('save_actions');
334
        $redirectUrl = $this->route;
335
336
        if (isset($actions[$saveAction])) {
337
            if ($actions[$saveAction]['redirect'] instanceof \Closure) {
338
                $redirectUrl = $actions[$saveAction]['redirect']($this, $request, $itemId);
339
            }
340
341
            //allow the save action to define default http_referrer (url for the save_and_back button)
342
            if (isset($actions[$saveAction]['referrer_url'])) {
343
                if ($actions[$saveAction]['referrer_url'] instanceof \Closure) {
344
                    $referrer_url = $actions[$saveAction]['referrer_url']($this, $request, $itemId);
345
                }
346
            }
347
        }
348
349
        // if the request is AJAX, return a JSON response
350
        if ($this->getRequest()->ajax()) {
351
            return response()->json([
352
                'success' => true,
353
                'data' => $this->entry,
354
                'redirect_url' => $redirectUrl,
355
                'referrer_url' => $referrer_url ?? false,
356
            ]);
357
        }
358
359
        if (isset($referrer_url)) {
360
            session()->flash('referrer_url_override', $referrer_url);
361
        }
362
363
        return \Redirect::to($redirectUrl);
364
    }
365
366
    /**
367
     * This functions register Backpack default save actions into CRUD.
368
     *
369
     * @return array
370
     */
371
    public function setupDefaultSaveActions()
372
    {
373
        $defaultSaveActions = [
374
            [
375
                'name' => 'save_and_back',
376
                'visible' => function ($crud) {
377
                    return $crud->hasAccess('list');
378
                },
379
                'redirect' => function ($crud, $request, $itemId = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $itemId is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

379
                'redirect' => function ($crud, $request, /** @scrutinizer ignore-unused */ $itemId = null) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
380
                    return $request->has('_http_referrer') ? $request->get('_http_referrer') : $crud->route;
381
                },
382
                'button_text' => trans('backpack::crud.save_action_save_and_back'),
383
            ],
384
            [
385
                'name' => 'save_and_edit',
386
                'visible' => function ($crud) {
387
                    return $crud->hasAccess('update');
388
                },
389
                'redirect' => function ($crud, $request, $itemId = null) {
390
                    $itemId = $itemId ?: $request->get('id');
391
                    $redirectUrl = $crud->route.'/'.$itemId.'/edit';
392
                    if ($request->has('_locale')) {
393
                        $redirectUrl .= '?_locale='.$request->get('_locale');
394
                    }
395
                    if ($request->has('_current_tab')) {
396
                        $redirectUrl = $redirectUrl.'#'.$request->get('_current_tab');
397
                    }
398
399
                    return $redirectUrl;
400
                },
401
                'referrer_url' => function ($crud, $request, $itemId) {
402
                    return url($crud->route.'/'.$itemId.'/edit');
403
                },
404
                'button_text' => trans('backpack::crud.save_action_save_and_edit'),
405
            ],
406
            [
407
                'name' => 'save_and_new',
408
                'visible' => function ($crud) {
409
                    return $crud->hasAccess('create');
410
                },
411
                'redirect' => function ($crud, $request, $itemId = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

411
                'redirect' => function ($crud, /** @scrutinizer ignore-unused */ $request, $itemId = null) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $itemId is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

411
                'redirect' => function ($crud, $request, /** @scrutinizer ignore-unused */ $itemId = null) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $crud is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

411
                'redirect' => function (/** @scrutinizer ignore-unused */ $crud, $request, $itemId = null) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
412
                    return $this->route.'/create';
413
                },
414
                'button_text' => trans('backpack::crud.save_action_save_and_new'),
415
            ],
416
        ];
417
418
        $this->addSaveActions($defaultSaveActions);
419
    }
420
}
421