Uccello::getDatatableColumns()   D
last analyzed

Complexity

Conditions 15
Paths 219

Size

Total Lines 75
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 41
c 2
b 0
f 0
dl 0
loc 75
rs 4.8458
cc 15
nc 219
nop 3

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 Uccello\Core\Helpers;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Support\Facades\Auth;
7
use Illuminate\Support\Facades\Cache;
8
use Uccello\Core\Models\Domain;
9
use Uccello\Core\Models\Module;
10
use Uccello\Core\Models\Uitype;
11
use Uccello\Core\Models\Displaytype;
12
use Uccello\Core\Models\Capability;
13
use Uccello\Core\Models\Entity;
14
use Uccello\Core\Models\Filter;
15
16
class Uccello
17
{
18
    /**
19
     * Returns true if multi domains are used, false else.
20
     *
21
     * @return void
22
     */
23
    public function useMultiDomains()
24
    {
25
        return env('UCCELLO_MULTI_DOMAINS', true) !== false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return env('UCCELLO_MULT...MAINS', true) !== false returns the type boolean which is incompatible with the documented return type void.
Loading history...
26
    }
27
28
    /**
29
     * Retrieve prefix and translate the given message.
30
     * If the translation does not exist try to find a default one.
31
     * If no translation exists display only the key.
32
     *
33
     * Priority:
34
     * 1 - Translation overrided in app
35
     * 2 - Translation in package
36
     * 3 - Default translation overrided in app
37
     * 4 - Default translation in uccello
38
     * 5 - No translation
39
     *
40
     * @param  string  $key
41
     * @param  Module|null  $module
42
     * @param  array   $replace
43
     * @param  string  $locale
44
     * @return \Illuminate\Contracts\Translation\Translator|string|array|null
45
     */
46
    public function trans($key = null, ?Module $module = null, $replace = [ ], $locale = null)
47
    {
48
        $translator = app('translator');
49
50
        if (is_null($key)) {
51
            return $translator;
52
        }
53
54
        // If $module is an instance of Module class, add a prefix before the key
55
        if (!is_null($module) && Module::class == get_class($module))
56
        {
57
            // By default prefix is same as the module's name
58
            $prefix = $module->name.'.';
0 ignored issues
show
Bug introduced by
The property name does not seem to exist on Uccello\Core\Models\Module. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
59
60
            // 1. Get translation in app
61
            $translation = $translator->trans($prefix.$key, $replace, $locale);
62
63
            if ($translation !== $prefix.$key) {
64
                return $translation;
65
            }
66
67
            // 2. Get translation in package
68
            if (!empty($module->package)) {
69
                // If a package name is defined add it before
70
                $prefix = $module->package.'::'.$prefix;
71
72
                $translation = $translator->trans($prefix.$key, $replace, $locale);
73
                if ($translation !== $prefix.$key) {
74
                    return $translation;
75
                }
76
            }
77
78
            // 3. Try with default translation in app
79
            $appDefaultTranslation = $translator->trans('default.'.$key, $replace, $locale);
80
            if ($appDefaultTranslation !== 'default.'.$key) { // If default translation exists then use it
81
                return $appDefaultTranslation;
82
            }
83
84
            // 4. Try with default translation in uccello
85
            $uccelloDefaultTranslation = $translator->trans('uccello::default.'.$key, $replace, $locale);
86
            if ($uccelloDefaultTranslation !== 'uccello::default.'.$key) { // If default translation exists then use it
87
                return $uccelloDefaultTranslation;
88
            }
89
90
            // 5. If translation does not exist, display only the key
91
            return $key;
92
        }
93
94
        // Default behaviour
95
        return $translator->trans($key, $replace, $locale);
96
    }
97
98
    /**
99
     * Detects which view it must use and returns the evaluated view contents.
100
     *
101
     * Priority:
102
     * 1 - Module view overrided in app
103
     * 2 - Default view overrided in app
104
     * 3 - Module view ovverrided in package
105
     * 4 - Default view defined in package
106
     * 5 - Module view ovverrided in uccello
107
     * 6 - Default view defined in uccello
108
     * 7 - Fallback view if defined
109
     *
110
     * @param string $package
111
     * @param Module $module
112
     * @param string $viewName
113
     * @param string|null $fallbackView
114
     * @return string|null
115
     */
116
    public function view(string $package, Module $module, string $viewName, ?string $fallbackView = null): ?string
117
    {
118
        // Module view overrided in app
119
        $appModuleView = 'uccello.modules.'.$module->name.'.'.$viewName;
0 ignored issues
show
Bug introduced by
The property name does not seem to exist on Uccello\Core\Models\Module. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
120
121
        // Default view overrided in app
122
        $appDefaultView = 'uccello.modules.default.'.$viewName;
123
124
        // Module view ovverrided in package
125
        $packageModuleView = $package.'::modules.'.$module->name.'.'.$viewName;
126
127
        // Default view defined in package
128
        $packageDefaultView = $package.'::modules.default.'.$viewName;
129
130
        // Module view ovverrided in uccello
131
        $uccelloModuleView = 'uccello::modules.'.$module->name.'.'.$viewName;
132
133
        // Default view defined in uccello
134
        $uccelloDefaultView = 'uccello::modules.default.'.$viewName;
135
136
        $viewToInclude = null;
137
        if (view()->exists($appModuleView)) {
138
            $viewToInclude = $appModuleView;
139
        } elseif (view()->exists($appDefaultView)) {
140
            $viewToInclude = $appDefaultView;
141
        } elseif (view()->exists($packageModuleView)) {
142
            $viewToInclude = $packageModuleView;
143
        } elseif (view()->exists($packageDefaultView)) {
144
            $viewToInclude = $packageDefaultView;
145
        } elseif (view()->exists($uccelloModuleView)) {
146
            $viewToInclude = $uccelloModuleView;
147
        } elseif (view()->exists($uccelloDefaultView)) {
148
            $viewToInclude = $uccelloDefaultView;
149
        } elseif (!is_null($fallbackView)) {
150
            $viewToInclude = $fallbackView;
151
        }
152
153
        return $viewToInclude;
154
    }
155
156
    /**
157
     * Makes route automaticaly and add module parameter.
158
     *
159
     * @param array|string $name
160
     * @param Domain|string|null $domain
161
     * @param Module|string|null $module
162
     * @param mixed $parameters
163
     * @param boolean $absolute
164
     * @return string
165
     */
166
    public function route($name, $domain = null, $module = null, $parameters = [ ], $absolute = true) : string
167
    {
168
        if (is_a($domain, Domain::class)) {
169
            $domain = $domain->slug;
170
        } else {
171
            $domain = $this->getDomain($domain)->slug ?? null;
172
        }
173
174
        if (is_a($module, Module::class)) {
175
            $module = $module->name;
0 ignored issues
show
Bug introduced by
The property name does not seem to exist on Uccello\Core\Models\Module. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
176
        } else {
177
            $module = $this->getModule($module)->name ?? null;
178
        }
179
180
        // Get route uri to check if domain and module parameters are needed
181
        $routeUri = \Route::getRoutes()->getByName($name)->uri ?? null;
0 ignored issues
show
Bug introduced by
It seems like $name can also be of type array; however, parameter $name of Illuminate\Routing\RouteCollection::getByName() does only seem to accept string, 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

181
        $routeUri = \Route::getRoutes()->getByName(/** @scrutinizer ignore-type */ $name)->uri ?? null;
Loading history...
182
183
        // Add domain to route if we use multi domains and if the parameter is needed
184
        if (!is_null($domain) && uccello()->useMultiDomains() && preg_match('`{domain}`', $routeUri)) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of uccello() 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...
185
            $parameters[ 'domain' ] = $domain;
186
        }
187
188
        // Add module to route if the parameter is needed
189
        if (!is_null($module) && preg_match('`{module}`', $routeUri)) {
190
            $parameters[ 'module' ] = $module;
191
        }
192
193
        return route($name, $parameters, $absolute);
194
    }
195
196
    /**
197
     * Returns the list of capabilities.
198
     *
199
     * @return \Illuminate\Database\Eloquent\Collection
200
     *
201
     * @see Uccello\Core\Models\Permission
202
     */
203
    public function getCapabilities(): Collection
204
    {
205
        return Capability::all();
206
    }
207
208
    /**
209
     * Get a domain instance by slug or id
210
     *
211
     * @param string|int $slugOrId
212
     * @return Domain|null
213
     */
214
    public function getDomain($slugOrId): ?Domain
215
    {
216
        if (is_numeric($slugOrId)) {
217
            return Domain::find($slugOrId);
218
        } else {
219
            return Domain::where('slug', (string)$slugOrId)->first();
220
        }
221
    }
222
223
    /**
224
     * Get a module instance by name or id
225
     *
226
     * @param string|int $nameOrId
227
     * @return Module|null
228
     */
229
    public function getModule($nameOrId): ?Module
230
    {
231
        if (!$nameOrId) {
232
            return null;
233
        }
234
235
        if (is_numeric($nameOrId)) {
236
            // Use cache
237
            $modules = Cache::rememberForever('modules_by_id', function () {
238
                $modulesGroupedById = collect();
239
                Module::all()->map(function($item) use($modulesGroupedById) {
240
                    $modulesGroupedById[$item->id] = $item;
241
                    return $modulesGroupedById;
242
                });
243
                return $modulesGroupedById;
244
            });
245
            return $modules[(string) $nameOrId] ?? null;
246
        } else {
247
            // Use cache
248
            $modules = Cache::rememberForever('modules_by_name', function () {
249
                $modulesGroupedByName = collect();
250
                Module::all()->map(function($item) use($modulesGroupedByName) {
251
                    $modulesGroupedByName[$item->name] = $item;
252
                    return $modulesGroupedByName;
253
                });
254
                return $modulesGroupedByName;
255
            });
256
            return $modules[(string) $nameOrId] ?? null;
257
        }
258
    }
259
260
    /**
261
     * Get an Uitype instance by name or id
262
     *
263
     * @param string|int $nameOrId
264
     * @return Uitype|null
265
     */
266
    public function getUitype($nameOrId): ?Uitype
267
    {
268
        if (!$nameOrId) {
269
            return null;
270
        }
271
272
        if (is_numeric($nameOrId)) {
273
            // Use cache
274
            $uitypes = Cache::rememberForever('uitypes_by_id', function () {
275
                $uitypesGroupedById = collect();
276
                Uitype::all()->map(function($item) use($uitypesGroupedById) {
277
                    $uitypesGroupedById[$item->id] = $item;
278
                    return $uitypesGroupedById;
279
                });
280
                return $uitypesGroupedById;
281
            });
282
            return $uitypes[(string) $nameOrId] ?? null;
283
        } else {
284
            // Use cache
285
            $uitypes = Cache::rememberForever('uitypes_by_name', function () {
286
                $uitypesGroupedByName = collect();
287
                Uitype::all()->map(function($item) use($uitypesGroupedByName) {
288
                    $uitypesGroupedByName[$item->name] = $item;
289
                    return $uitypesGroupedByName;
290
                });
291
                return $uitypesGroupedByName;
292
            });
293
            return $uitypes[(string) $nameOrId] ?? null;
294
        }
295
    }
296
297
    /**
298
     * Get a display type instance by name or id
299
     *
300
     * @param string|int $nameOrId
301
     * @return Uitype|null
302
     */
303
    public function getDisplaytype($nameOrId): ?Displaytype
304
    {
305
        if (!$nameOrId) {
306
            return null;
307
        }
308
309
        if (is_numeric($nameOrId)) {
310
            // Use cache
311
            $displaytypes = Cache::rememberForever('displaytypes_by_id', function () {
312
                $displaytypesGroupedById = collect();
313
                Displaytype::all()->map(function($item) use($displaytypesGroupedById) {
314
                    $displaytypesGroupedById[$item->id] = $item;
315
                    return $displaytypesGroupedById;
316
                });
317
                return $displaytypesGroupedById;
318
            });
319
            return $displaytypes[(string) $nameOrId] ?? null;
320
        } else {
321
            // Use cache
322
            $displaytypes = Cache::rememberForever('displaytypes_by_name', function () {
323
                $displaytypesGroupedByName = collect();
324
                Displaytype::all()->map(function($item) use($displaytypesGroupedByName) {
325
                    $displaytypesGroupedByName[$item->name] = $item;
326
                    return $displaytypesGroupedByName;
327
                });
328
                return $displaytypesGroupedByName;
329
            });
330
            return $displaytypes[(string) $nameOrId] ?? null;
331
        }
332
    }
333
334
    /**
335
     * Get a capability instance by name or id
336
     *
337
     * @param string|int $nameOrId
338
     * @return Uitype|null
339
     */
340
    public function getCapability($nameOrId): ?Capability
341
    {
342
        if (!$nameOrId) {
343
            return null;
344
        }
345
346
        if (is_numeric($nameOrId)) {
347
            // Use cache
348
            $capabilities = Cache::rememberForever('capabilities_by_id', function () {
349
                $capabilitiesGroupedById = collect();
350
                Capability::all()->map(function($item) use($capabilitiesGroupedById) {
351
                    $capabilitiesGroupedById[$item->id] = $item;
352
                    return $capabilitiesGroupedById;
353
                });
354
                return $capabilitiesGroupedById;
355
            });
356
            return $capabilities[(string) $nameOrId] ?? null;
357
        } else {
358
            // Use cache
359
            $capabilities = Cache::rememberForever('capabilities_by_name', function () {
360
                $capabilitiesGroupedByName = collect();
361
                Capability::all()->map(function($item) use($capabilitiesGroupedByName) {
362
                    $capabilitiesGroupedByName[$item->name] = $item;
363
                    return $capabilitiesGroupedByName;
364
                });
365
                return $capabilitiesGroupedByName;
366
            });
367
            return $capabilities[(string) $nameOrId] ?? null;
368
        }
369
    }
370
371
    /**
372
     * Returns all domains without parents
373
     *
374
     * @return Collection
375
     */
376
    public function getRootDomains(): Collection
377
    {
378
        return Domain::getRoots()->get();
379
    }
380
381
    /**
382
     * Get last domain visited by the connected user, or the first one available
383
     * Priority:
384
     * 1. Last domain visited
385
     * 2. Domain where the user was created into
386
     * 3. First root domain
387
     *
388
     * @return Domain|null
389
     */
390
    public function getLastOrDefaultDomain(): ?Domain
391
    {
392
        $domain = Auth::user()->lastDomain ?? Auth::user()->domain ?? null; // On login page user is not authenticated
0 ignored issues
show
Bug introduced by
Accessing lastDomain on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
Bug introduced by
Accessing domain on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
393
394
        if (!$domain) {
395
            $domain = $this->getRootDomains()[ 0 ];
396
        }
397
398
        return $domain;
399
    }
400
401
    /**
402
     * Retrieve columns to display in a datatable table
403
     *
404
     * @param Module $module
405
     * @param integer $filterId
406
     * @param string $type
407
     * @return array
408
     */
409
    public function getDatatableColumns(Module $module, $filterId=null, $type='list'): array
410
    {
411
        $columns = [ ];
412
413
        // Get default filter
414
        if ($filterId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filterId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
415
            $filter = Filter::find($filterId);
416
        } else {
417
            $filter = Filter::where('module_id', $module->id)
418
                ->where('type', $type)
419
                ->first();
420
421
            // If there is not result, try with type = list
422
            if (empty($filter) && $type !== 'list') {
423
                $filter = Filter::where('module_id', $module->id)
424
                ->where('type', 'list')
425
                ->first();
426
            }
427
        }
428
429
        if (empty($filter)) {
430
            return [ ];
431
        }
432
433
        $fieldsAdded = [ ];
434
435
        $seeDescendants = request()->hasSession() && request()->session()->get('descendants') ? true : false;
436
437
        // Get filter fields
438
        foreach ($filter->columns as $fieldName) {
0 ignored issues
show
Bug introduced by
The property columns does not seem to exist on Uccello\Core\Models\Filter. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
439
            $field = $module->fields->where('name', $fieldName)->first();
440
441
            // If the field does not exist or is not listable, continue
442
            if (!$field  || !$field->isListable() || in_array($fieldName, $fieldsAdded)) {
443
                continue;
444
            }
445
446
            $uitype = uitype($field->uitype_id);
447
448
            // Add the field as a new column
449
            $columns[ ] = [
450
                'name' => $field->name,
451
                'db_column' => $field->column,
452
                'uitype' => $uitype->name,
453
                'package' => $uitype->package,
454
                'data' => $field->data,
455
                'visible' => true
456
            ];
457
458
            $fieldsAdded[ ] = $fieldName;
459
        }
460
461
        // Get all other fields
462
        $otherFields = $module->fields->whereNotIn('name', $fieldsAdded);
463
464
        foreach ($otherFields as $field) {
465
            // If the field is not listable, continue
466
            if (!$field->isListable()) {
467
                continue;
468
            }
469
470
            $uitype = uitype($field->uitype_id);
471
472
            // Add the field as a new column
473
            $columns[ ] = [
474
                'name' => $field->name,
475
                'db_column' => $field->column,
476
                'uitype' => $uitype->name,
477
                'package' => $uitype->package,
478
                'data' => $field->data,
479
                'visible' => $field->name === 'domain' && $seeDescendants ? true : false
480
            ];
481
        }
482
483
        return $columns;
484
    }
485
486
    /**
487
     * Returns the module's default filter according to the type.
488
     *
489
     * @param Module $module
490
     * @param string $type
491
     * @return Filter
492
     */
493
    public function getDefaultFilter(Module $module, $type="list")
494
    {
495
        $filter = Filter::where('module_id', $module->id)
496
        ->where('type', $type)
497
        ->first();
498
499
        // If there is not result, try with type = list
500
        if (empty($filter) && $type !== 'list') {
501
            $filter = Filter::where('module_id', $module->id)
502
            ->where('type', 'list')
503
            ->first();
504
        }
505
506
        return $filter;
507
    }
508
509
    /**
510
     * Returns a record attribute value.
511
     * It is able to follow a complex path according to models definition (e.g. 'domain.parent.name')
512
     *
513
     * @param Object $record
514
     * @param string $attribute
515
     * @return string|Object|Array|null
516
     */
517
    public function getRecordAttribute($record, string $attribute) {
518
519
        $attributeParts = explode('.', $attribute);
520
521
        if (count($attributeParts) > 0) {
522
            $value = $record;
523
524
            foreach ($attributeParts as $part) {
525
                // Get attribute value if exists
526
                if (isset($value->{$part})) {
527
                    $value = $value->{$part};
528
                }
529
                // If property does not exist return an empty value
530
                else {
531
                    $value = null;
532
                    break;
533
                }
534
            }
535
        } else {
536
            $value = $record->{$attribute};
537
        }
538
539
        return $value;
540
    }
541
542
    /**
543
     * Retrieves a record by its id or uuid
544
     *
545
     * @param int|string $idOrUuid
546
     * @param string|null $className
547
     * @return mixed
548
     */
549
    public function getRecordByIdOrUuid($idOrUuid, $className = null)
550
    {
551
        $record = null;
552
553
        if (is_numeric($idOrUuid) && !empty($className)) {
554
            $record = $className::find($idOrUuid);
555
        } else {
556
            $record = $this->getRecordByUuid($idOrUuid);
557
        }
558
559
        return $record;
560
    }
561
562
    /**
563
     * Retrieves a record by its uuid
564
     *
565
     * @param string $uuid
566
     * @return mixed
567
     */
568
    public function getRecordByUuid($uuid)
569
    {
570
        $record = null;
571
572
        if (!empty($uuid)) {
573
            $entity = Entity::find($uuid);
574
            if ($entity) {
575
                $record = $entity->record;
576
            }
577
        }
578
579
        return $record;
580
    }
581
582
    /**
583
     * Checks if the module is related to a Model.
584
     *
585
     * @return boolean
586
     */
587
    public function isCrudModule(Module $module)
588
    {
589
        return !empty($module->model_class) && class_exists($module->model_class);
0 ignored issues
show
Bug introduced by
The property model_class does not seem to exist on Uccello\Core\Models\Module. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
590
    }
591
592
    /**
593
     * Checks if the module extends \Gzero\EloquentTree\Model\Tree.
594
     *
595
     * @return boolean
596
     */
597
    public function isTreeModule(Module $module)
598
    {
599
        $modelClass = $module->model_class;
0 ignored issues
show
Bug introduced by
The property model_class does not seem to exist on Uccello\Core\Models\Module. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
600
601
        return is_subclass_of((new $modelClass), \Gzero\EloquentTree\Model\Tree::class);
0 ignored issues
show
Bug introduced by
The type Gzero\EloquentTree\Model\Tree 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...
602
    }
603
}