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 — datatable-single-component ( b25568...6a92d6 )
by Pedro
12:01
created

CrudPanel::group()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel;
4
5
use Backpack\CRUD\app\Library\CrudPanel\Traits\Access;
6
use Backpack\CRUD\app\Library\CrudPanel\Traits\AutoFocus;
7
use Backpack\CRUD\app\Library\CrudPanel\Traits\AutoSet;
8
use Backpack\CRUD\app\Library\CrudPanel\Traits\Buttons;
9
use Backpack\CRUD\app\Library\CrudPanel\Traits\Columns;
10
use Backpack\CRUD\app\Library\CrudPanel\Traits\Create;
11
use Backpack\CRUD\app\Library\CrudPanel\Traits\Delete;
12
use Backpack\CRUD\app\Library\CrudPanel\Traits\Errors;
13
use Backpack\CRUD\app\Library\CrudPanel\Traits\FakeColumns;
14
use Backpack\CRUD\app\Library\CrudPanel\Traits\FakeFields;
15
use Backpack\CRUD\app\Library\CrudPanel\Traits\Fields;
16
use Backpack\CRUD\app\Library\CrudPanel\Traits\Filters;
17
use Backpack\CRUD\app\Library\CrudPanel\Traits\HasViewNamespaces;
18
use Backpack\CRUD\app\Library\CrudPanel\Traits\HeadingsAndTitles;
19
use Backpack\CRUD\app\Library\CrudPanel\Traits\Input;
20
use Backpack\CRUD\app\Library\CrudPanel\Traits\Macroable;
21
use Backpack\CRUD\app\Library\CrudPanel\Traits\MorphRelationships;
22
use Backpack\CRUD\app\Library\CrudPanel\Traits\Operations;
23
use Backpack\CRUD\app\Library\CrudPanel\Traits\Query;
24
use Backpack\CRUD\app\Library\CrudPanel\Traits\Read;
25
use Backpack\CRUD\app\Library\CrudPanel\Traits\Relationships;
0 ignored issues
show
Bug introduced by
The type Backpack\CRUD\app\Librar...el\Traits\Relationships 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...
26
use Backpack\CRUD\app\Library\CrudPanel\Traits\Reorder;
27
use Backpack\CRUD\app\Library\CrudPanel\Traits\SaveActions;
28
use Backpack\CRUD\app\Library\CrudPanel\Traits\Search;
29
use Backpack\CRUD\app\Library\CrudPanel\Traits\Settings;
30
use Backpack\CRUD\app\Library\CrudPanel\Traits\Tabs;
31
use Backpack\CRUD\app\Library\CrudPanel\Traits\Update;
32
use Backpack\CRUD\app\Library\CrudPanel\Traits\Validation;
33
use Backpack\CRUD\app\Library\CrudPanel\Traits\Views;
34
use Backpack\CRUD\CrudManager;
35
use Exception;
36
use Illuminate\Database\Eloquent\Collection;
37
use Illuminate\Database\Eloquent\Model;
38
use Illuminate\Database\Eloquent\Relations\Relation;
39
use Illuminate\Http\Request;
40
use Illuminate\Support\Arr;
41
use Illuminate\Support\Facades\Route;
42
43
class CrudPanel
44
{
45
    // load all the default CrudPanel features
46
    use Create, Read, Search, Update, Delete, Input, Errors, Reorder, Access, Columns, Fields, Query, Buttons, AutoSet, FakeFields, FakeColumns, AutoFocus, Filters, Tabs, Views, Validation, HeadingsAndTitles, Operations, SaveActions, Settings, Relationships, HasViewNamespaces, MorphRelationships;
47
48
    // allow developers to add their own closures to this object
49
    use Macroable;
50
51
    // --------------
52
    // CRUD variables
53
    // --------------
54
    // These variables are passed to the CRUD views, inside the $crud variable.
55
    // All variables are public, so they can be modified from your EntityCrudController.
56
    // All functions and methods are also public, so they can be used in your EntityCrudController to modify these variables.
57
58
    public $model = "\App\Models\Entity"; // what's the namespace for your entity's model
59
60
    public $route; // what route have you defined for your entity? used for links.
61
62
    public $entity_name = 'entry'; // what name will show up on the buttons, in singural (ex: Add entity)
63
64
    public $entity_name_plural = 'entries'; // what name will show up on the buttons, in plural (ex: Delete 5 entities)
65
66
    public $entry;
67
68
    protected $request;
69
70
    public $initialized = false;
71
72
    public $crudController;
73
74
    // The following methods are used in CrudController or your EntityCrudController to manipulate the variables above.
75
76
    public function __construct()
77
    {
78
    }
79
80
    public function isInitialized()
81
    {
82
        return $this->initialized;
83
    }
84
85
    public function initialize(string $controller, $request): self
86
    {
87
        $this->setRequest($request);
88
        $this->setCrudController($controller);    
89
        
90
        return $this;
91
    }
92
93
    /**
94
     * Set the request instance for this CRUD.
95
     *
96
     * @param  Request  $request
97
     */
98
    public function setRequest($request = null): self
99
    {
100
        $this->request = $request ?? \Request::instance();
101
102
        return $this;
103
    }
104
105
    /**
106
     * Get the request instance for this CRUD.
107
     *
108
     * @return Request
109
     */
110
    public function getRequest()
111
    {
112
        return $this->request;
113
    }
114
115
    // ------------------------------------------------------
116
    // BASICS - model, route, entity_name, entity_name_plural
117
    // ------------------------------------------------------
118
119
    /**
120
     * This function binds the CRUD to its corresponding Model (which extends Eloquent).
121
     * All Create-Read-Update-Delete operations are done using that Eloquent Collection.
122
     *
123
     * @param  string  $model_namespace  Full model namespace. Ex: App\Models\Article
124
     *
125
     * @throws Exception in case the model does not exist
126
     */
127
    public function setModel($model_namespace)
128
    {
129
        if (! class_exists($model_namespace)) {
130
            throw new Exception('The model does not exist.', 500);
131
        }
132
133
        if (! method_exists($model_namespace, 'hasCrudTrait')) {
134
            throw new Exception('Please use CrudTrait on the model.', 500);
135
        }
136
137
        $this->model = new $model_namespace();
138
        $this->query = clone $this->totalQuery = $this->model->select('*');
139
        $this->entry = null;
140
    }
141
142
    /**
143
     * Get the corresponding Eloquent Model for the CrudController, as defined with the setModel() function.
144
     *
145
     * @return string|\Illuminate\Database\Eloquent\Model
146
     */
147
    public function getModel()
148
    {
149
        return $this->model;
150
    }
151
152
    /**
153
     * Get the database connection, as specified in the .env file or overwritten by the property on the model.
154
     *
155
     * @return \Illuminate\Database\Schema\Builder
156
     */
157
    private function getSchema()
158
    {
159
        return $this->getModel()->getConnection()->getSchemaBuilder();
160
    }
161
162
    public function setCrudController(string $crudController)
163
    {
164
        $this->crudController = $crudController;
165
    }
166
167
    /**
168
     * Check if the database connection driver is using mongodb.
169
     *
170
     * DEPRECATION NOTICE: This method is no longer used and will be removed in future versions of Backpack
171
     *
172
     * @deprecated
173
     *
174
     * @codeCoverageIgnore
175
     *
176
     * @return bool
177
     */
178
    private function driverIsMongoDb()
179
    {
180
        return $this->getSchema()->getConnection()->getConfig()['driver'] === 'mongodb';
181
    }
182
183
    /**
184
     * Check if the database connection is any sql driver.
185
     *
186
     * @return bool
187
     */
188
    private function driverIsSql()
189
    {
190
        $driver = $this->getSchema()->getConnection()->getConfig('driver');
191
192
        return in_array($driver, $this->getSqlDriverList());
193
    }
194
195
    /**
196
     * Get SQL driver list.
197
     *
198
     * @return array
199
     */
200
    public function getSqlDriverList()
201
    {
202
        return ['mysql', 'sqlsrv', 'sqlite', 'pgsql', 'mariadb'];
203
    }
204
205
    /**
206
     * Set the route for this CRUD.
207
     * Ex: admin/article.
208
     *
209
     * @param  string  $route  Route name.
210
     */
211
    public function setRoute($route)
212
    {
213
        $this->route = ltrim($route, '/');
214
    }
215
216
    /**
217
     * Set the route for this CRUD using the route name.
218
     * Ex: admin.article.
219
     *
220
     * @param  string  $route  Route name.
221
     * @param  array  $parameters  Parameters.
222
     *
223
     * @throws Exception
224
     */
225
    public function setRouteName($route, $parameters = [])
226
    {
227
        $route = ltrim($route, '.');
228
229
        $complete_route = $route.'.index';
230
231
        if (! \Route::has($complete_route)) {
232
            throw new Exception('There are no routes for this route name.', 404);
233
        }
234
235
        $this->route = route($complete_route, $parameters);
236
    }
237
238
    /**
239
     * Get the current CrudController route.
240
     *
241
     * Can be defined in the CrudController with:
242
     * - $this->crud->setRoute(config('backpack.base.route_prefix').'/article')
243
     * - $this->crud->setRouteName(config('backpack.base.route_prefix').'.article')
244
     * - $this->crud->route = config('backpack.base.route_prefix')."/article"
245
     *
246
     * @return string
247
     */
248
    public function getRoute()
249
    {
250
        return $this->route;
251
    }
252
253
    /**
254
     * Set the entity name in singular and plural.
255
     * Used all over the CRUD interface (header, add button, reorder button, breadcrumbs).
256
     *
257
     * @param  string  $singular  Entity name, in singular. Ex: article
258
     * @param  string  $plural  Entity name, in plural. Ex: articles
259
     */
260
    public function setEntityNameStrings($singular, $plural)
261
    {
262
        $this->entity_name = $singular;
263
        $this->entity_name_plural = $plural;
264
    }
265
266
    // -----------------------------------------------
267
    // ACTIONS - the current operation being processed
268
    // -----------------------------------------------
269
270
    /**
271
     * Get the action being performed by the controller,
272
     * including middleware names, route name, method name,
273
     * namespace, prefix, etc.
274
     *
275
     * @return string The EntityCrudController route action array.
276
     */
277
    public function getAction()
278
    {
279
        return $this->getRequest()->route()->getAction();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getRequest()->route()->getAction() also could return the type array which is incompatible with the documented return type string.
Loading history...
280
    }
281
282
    /**
283
     * Get the full name of the controller method
284
     * currently being called (including namespace).
285
     *
286
     * @return string The EntityCrudController full method name with namespace.
287
     */
288
    public function getActionName()
289
    {
290
        return $this->getRequest()->route()->getActionName();
291
    }
292
293
    /**
294
     * Get the name of the controller method
295
     * currently being called.
296
     *
297
     * @return string The EntityCrudController method name.
298
     */
299
    public function getActionMethod()
300
    {
301
        return $this->getRequest()->route()->getActionMethod();
302
    }
303
304
    /**
305
     * Check if the controller method being called
306
     * matches a given string.
307
     *
308
     * @param  string  $methodName  Name of the method (ex: index, create, update)
309
     * @return bool Whether the condition is met or not.
310
     */
311
    public function actionIs($methodName)
312
    {
313
        return $methodName === $this->getActionMethod();
314
    }
315
316
    // ----------------------------------
317
    // Miscellaneous functions or methods
318
    // ----------------------------------
319
320
    /**
321
     * Return the first element in an array that has the given 'type' attribute.
322
     *
323
     * @param  string  $type
324
     * @param  array  $array
325
     * @return array
326
     */
327
    public function getFirstOfItsTypeInArray($type, $array)
328
    {
329
        return Arr::first($array, function ($item) use ($type) {
330
            return $item['type'] == $type;
331
        });
332
    }
333
334
    /**
335
     * TONE FUNCTIONS - UNDOCUMENTED, UNTESTED, SOME MAY BE USED IN THIS FILE.
336
     *
337
     * TODO:
338
     * - figure out if they are really needed
339
     * - comments inside the function to explain how they work
340
     * - write docblock for them
341
     * - place in the correct section above (CREATE, READ, UPDATE, DELETE, ACCESS, MANIPULATION)
342
     *
343
     * @deprecated
344
     *
345
     * @codeCoverageIgnore
346
     */
347
    public function sync($type, $fields, $attributes)
348
    {
349
        if (! empty($this->{$type})) {
350
            $this->{$type} = array_map(function ($field) use ($fields, $attributes) {
351
                if (in_array($field['name'], (array) $fields)) {
352
                    $field = array_merge($field, $attributes);
353
                }
354
355
                return $field;
356
            }, $this->{$type});
357
        }
358
    }
359
360
    /**
361
     * Get the Eloquent Model name from the given relation definition string.
362
     *
363
     * @example For a given string 'company' and a relation between App/Models/User and App/Models/Company, defined by a
364
     *          company() method on the user model, the 'App/Models/Company' string will be returned.
365
     * @example For a given string 'company.address' and a relation between App/Models/User, App/Models/Company and
366
     *          App/Models/Address defined by a company() method on the user model and an address() method on the
367
     *          company model, the 'App/Models/Address' string will be returned.
368
     *
369
     * @param  string  $relationString  Relation string. A dot notation can be used to chain multiple relations.
370
     * @param  int  $length  Optionally specify the number of relations to omit from the start of the relation string. If
371
     *                       the provided length is negative, then that many relations will be omitted from the end of the relation
372
     *                       string.
373
     * @param  Model  $model  Optionally specify a different model than the one in the crud object.
374
     * @return string Relation model name.
375
     */
376
    public function getRelationModel($relationString, $length = null, $model = null)
377
    {
378
        $relationArray = explode('.', $relationString);
379
380
        if (! isset($length)) {
381
            $length = count($relationArray);
382
        }
383
384
        if (! isset($model)) {
385
            $model = $this->model;
386
        }
387
388
        $result = array_reduce(array_splice($relationArray, 0, $length), function ($obj, $method) {
389
            try {
390
                $result = $obj->$method();
391
                if (! $result instanceof Relation) {
392
                    throw new Exception('Not a relation');
393
                }
394
395
                return $result->getRelated();
396
            } catch (Exception $e) {
397
                return $obj;
398
            }
399
        }, $model);
400
401
        return get_class($result);
402
    }
403
404
    /**
405
     * Get the given attribute from a model or models resulting from the specified relation string (eg: the list of streets from
406
     * the many addresses of the company of a given user).
407
     *
408
     * @param  Model  $model  Model (eg: user).
409
     * @param  string  $relationString  Model relation. Can be a string representing the name of a relation method in the given
410
     *                                  Model or one from a different Model through multiple relations. A dot notation can be used to specify
411
     *                                  multiple relations (eg: user.company.address).
412
     * @param  string  $attribute  The attribute from the relation model (eg: the street attribute from the address model).
413
     * @return array An array containing a list of attributes from the resulting model.
414
     */
415
    public function getRelatedEntriesAttributes($model, $relationString, $attribute)
416
    {
417
        $endModels = $this->getRelatedEntries($model, $relationString);
418
        $attributes = [];
419
        foreach ($endModels as $model => $entries) {
0 ignored issues
show
introduced by
$model is overwriting one of the parameters of this function.
Loading history...
420
            /** @var Model $model_instance */
421
            $model_instance = new $model();
422
            $modelKey = $model_instance->getKeyName();
423
424
            if (is_array($entries)) {
425
                //if attribute does not exist in main array we have more than one entry OR the attribute
426
                //is an accessor that is not in $appends property of model.
427
                if (! isset($entries[$attribute])) {
428
                    //we first check if we don't have the attribute because it's an accessor that is not in appends.
429
                    if ($model_instance->hasGetMutator($attribute) && isset($entries[$modelKey])) {
430
                        $entry_in_database = $model_instance->find($entries[$modelKey]);
431
                        $attributes[$entry_in_database->{$modelKey}] = $this->parseTranslatableAttributes($model_instance, $attribute, $entry_in_database->{$attribute});
432
                    } else {
433
                        //we have multiple entries
434
                        //for each entry we check if $attribute exists in array or try to check if it's an accessor.
435
                        foreach ($entries as $entry) {
436
                            if (isset($entry[$attribute])) {
437
                                $attributes[$entry[$modelKey]] = $this->parseTranslatableAttributes($model_instance, $attribute, $entry[$attribute]);
438
                            } else {
439
                                if ($model_instance->hasGetMutator($attribute)) {
440
                                    $entry_in_database = $model_instance->find($entry[$modelKey]);
441
                                    $attributes[$entry_in_database->{$modelKey}] = $this->parseTranslatableAttributes($model_instance, $attribute, $entry_in_database->{$attribute});
442
                                }
443
                            }
444
                        }
445
                    }
446
                } else {
447
                    //if we have the attribute we just return it, does not matter if it is direct attribute or an accessor added in $appends.
448
                    $attributes[$entries[$modelKey]] = $this->parseTranslatableAttributes($model_instance, $attribute, $entries[$attribute]);
449
                }
450
            }
451
        }
452
453
        return $attributes;
454
    }
455
456
    /**
457
     * Parse translatable attributes from a model or models resulting from the specified relation string.
458
     *
459
     * @param  Model  $model  Model (eg: user).
460
     * @param  string  $attribute  The attribute from the relation model (eg: the street attribute from the address model).
461
     * @param  string  $value  Attribute value translatable or not
462
     * @return string A string containing the translated attributed based on app()->getLocale()
463
     */
464
    public function parseTranslatableAttributes($model, $attribute, $value)
465
    {
466
        if (! method_exists($model, 'isTranslatableAttribute')) {
467
            return $value;
468
        }
469
470
        if (! $model->isTranslatableAttribute($attribute)) {
471
            return $value;
472
        }
473
474
        if (! is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
475
            $decodedAttribute = json_decode($value, true) ?? ($value !== null ? [$value] : []);
0 ignored issues
show
introduced by
The condition $value !== null is always true.
Loading history...
476
        } else {
477
            $decodedAttribute = $value;
478
        }
479
480
        if (is_array($decodedAttribute) && ! empty($decodedAttribute)) {
481
            if (isset($decodedAttribute[app()->getLocale()])) {
0 ignored issues
show
introduced by
The method getLocale() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

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

481
            if (isset($decodedAttribute[app()->/** @scrutinizer ignore-call */ getLocale()])) {
Loading history...
482
                return $decodedAttribute[app()->getLocale()];
483
            } else {
484
                return Arr::first($decodedAttribute);
485
            }
486
        }
487
488
        return $value;
489
    }
490
491
    public function setLocaleOnModel(Model $model)
492
    {
493
        $useFallbackLocale = $this->shouldUseFallbackLocale();
494
495
        if (method_exists($model, 'translationEnabled') && $model->translationEnabled()) {
496
            $locale = $this->getRequest()->input('_locale', app()->getLocale());
497
            if (in_array($locale, array_keys($model->getAvailableLocales()))) {
0 ignored issues
show
Bug introduced by
It seems like $model->getAvailableLocales() can also be of type Illuminate\Database\Eloquent\Builder and Illuminate\Database\Eloq...gHasThroughRelationship; however, parameter $array of array_keys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

497
            if (in_array($locale, array_keys(/** @scrutinizer ignore-type */ $model->getAvailableLocales()))) {
Loading history...
498
                $model->setLocale(! is_bool($useFallbackLocale) ? $useFallbackLocale : $locale);
0 ignored issues
show
introduced by
The condition is_bool($useFallbackLocale) is always true.
Loading history...
499
                $model->useFallbackLocale = (bool) $useFallbackLocale;
500
            }
501
        }
502
503
        return $model;
504
    }
505
506
    /**
507
     * Traverse the tree of relations for the given model, defined by the given relation string, and return the ending
508
     * associated model instance or instances.
509
     *
510
     * @param  Model  $model  The CRUD model.
511
     * @param  string  $relationString  Relation string. A dot notation can be used to chain multiple relations.
512
     * @return array An array of the associated model instances defined by the relation string.
513
     */
514
    private function getRelatedEntries($model, $relationString)
515
    {
516
        $relationArray = explode('.', $this->getOnlyRelationEntity(['entity' => $relationString]));
517
        $firstRelationName = Arr::first($relationArray);
518
        $relation = $model->{$firstRelationName};
519
520
        $results = [];
521
        if (! is_null($relation)) {
522
            if ($relation instanceof Collection) {
523
                $currentResults = $relation->all();
524
            } elseif (is_array($relation)) {
525
                $currentResults = $relation;
526
            } elseif ($relation instanceof Model) {
527
                $currentResults = [$relation];
528
            } else {
529
                $currentResults = [];
530
            }
531
532
            array_shift($relationArray);
533
534
            if (! empty($relationArray)) {
535
                foreach ($currentResults as $currentResult) {
536
                    $results = array_merge_recursive($results, $this->getRelatedEntries($currentResult, implode('.', $relationArray)));
537
                }
538
            } else {
539
                $relatedClass = get_class($model->{$firstRelationName}()->getRelated());
540
                $results[$relatedClass] = $currentResults;
541
            }
542
        }
543
544
        return $results;
545
    }
546
547
    /**
548
     * Allow to add an attribute to multiple fields/columns/filters/buttons at same time.
549
     *
550
     * Using the fluent syntax allow the developer to add attributes to multiple fields at the same time. Eg:
551
     *
552
     * - CRUD::group(CRUD::field('price')->type('number'), CRUD::field('title')->type('text'))->tab('both_on_same_tab');
553
     *
554
     * @param  mixed fluent syntax objects.
0 ignored issues
show
Bug introduced by
The type Backpack\CRUD\app\Library\CrudPanel\fluent 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...
555
     * @return CrudObjectGroup
556
     */
557
    public function group(...$objects)
558
    {
559
        return new CrudObjectGroup(...$objects);
560
    }
561
}
562