Completed
Push — master ( 97eddd...5cf35a )
by Jonathan
19:50 queued 09:35
created

Uccello::getDatatableColumns()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 45
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 25
nc 12
nop 3
dl 0
loc 45
rs 8.5866
c 0
b 0
f 0
1
<?php
2
3
namespace Uccello\Core\Helpers;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Support\Facades\Auth;
7
use Uccello\Core\Models\Domain;
8
use Uccello\Core\Models\Module;
9
use Uccello\Core\Models\Permission;
10
use Uccello\Core\Models\Uitype;
11
use Uccello\Core\Models\Displaytype;
12
use Uccello\Core\Models\Capability;
13
use Uccello\Core\Models\Filter;
14
15
class Uccello
16
{
17
    /**
18
     * Returns true if multi domains are used, false else.
19
     *
20
     * @return void
21
     */
22
    public function useMultiDomains()
23
    {
24
        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...
25
    }
26
27
    /**
28
     * Retrieve prefix and translate the given message.
29
     * If the translation does not exist try to find a default one.
30
     * If no translation exists display only the key.
31
     *
32
     * Priority:
33
     * 1 - Translation overrided in app
34
     * 2 - Translation in package
35
     * 3 - Default translation overrided in app
36
     * 4 - Default translation in uccello
37
     * 5 - No translation
38
     *
39
     * @param  string  $key
40
     * @param  Module|null  $module
41
     * @param  array   $replace
42
     * @param  string  $locale
43
     * @return \Illuminate\Contracts\Translation\Translator|string|array|null
44
     */
45
    public function trans($key = null, ?Module $module = null, $replace = [ ], $locale = null)
46
    {
47
        $translator = app('translator');
48
49
        if (is_null($key)) {
50
            return $translator;
51
        }
52
53
        // If $module is an instance of Module class, add a prefix before the key
54
        if (!is_null($module) && Module::class == get_class($module))
55
        {
56
            // By default prefix is same as the module's name
57
            $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...
58
59
            // 1. Get translation in app
60
            $translation = $translator->trans($prefix.$key, $replace, $locale);
61
62
            if ($translation !== $prefix.$key) {
63
                return $translation;
64
            }
65
66
            // 2. Get translation in package
67
            if (!empty($module->package)) {
68
                // If a package name is defined add it before
69
                $prefix = $module->package.'::'.$prefix;
70
71
                $translation = $translator->trans($prefix.$key, $replace, $locale);
72
                if ($translation !== $prefix.$key) {
73
                    return $translation;
74
                }
75
            }
76
77
            // 3. Try with default translation in app
78
            $appDefaultTranslation = $translator->trans('default.'.$key, $replace, $locale);
79
            if ($appDefaultTranslation !== 'default.'.$key) { // If default translation exists then use it
80
                return $appDefaultTranslation;
81
            }
82
83
            // 4. Try with default translation in uccello
84
            $uccelloDefaultTranslation = $translator->trans('uccello::default.'.$key, $replace, $locale);
85
            if ($uccelloDefaultTranslation !== 'uccello::default.'.$key) { // If default translation exists then use it
86
                return $uccelloDefaultTranslation;
87
            }
88
89
            // 5. If translation does not exist, display only the key
90
            return $key;
91
        }
92
93
        // Default behaviour
94
        return $translator->trans($key, $replace, $locale);
95
    }
96
97
    /**
98
     * Detects which view it must use and returns the evaluated view contents.
99
     *
100
     * Priority:
101
     * 1 - Module view overrided in app
102
     * 2 - Default view overrided in app
103
     * 3 - Module view ovverrided in package
104
     * 4 - Default view defined in package
105
     * 5 - Module view ovverrided in uccello
106
     * 6 - Default view defined in uccello
107
     * 7 - Fallback view if defined
108
     *
109
     * @param string $package
110
     * @param Module $module
111
     * @param string $viewName
112
     * @param string|null $fallbackView
113
     * @return string|null
114
     */
115
    public function view(string $package, Module $module, string $viewName, ?string $fallbackView = null): ?string
116
    {
117
        // Module view overrided in app
118
        $appModuleView = '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...
119
120
        // Default view overrided in app
121
        $appDefaultView = 'modules.default.'.$viewName;
122
123
        // Module view ovverrided in package
124
        $packageModuleView = $package.'::modules.'.$module->name.'.'.$viewName;
125
126
        // Default view defined in package
127
        $packageDefaultView = $package.'::modules.default.'.$viewName;
128
129
        // Module view ovverrided in uccello
130
        $uccelloModuleView = 'uccello::modules.'.$module->name.'.'.$viewName;
131
132
        // Default view defined in uccello
133
        $uccelloDefaultView = 'uccello::modules.default.'.$viewName;
134
135
        $viewToInclude = null;
136
        if (view()->exists($appModuleView)) {
137
            $viewToInclude = $appModuleView;
138
        } elseif (view()->exists($appDefaultView)) {
139
            $viewToInclude = $appDefaultView;
140
        } elseif (view()->exists($packageModuleView)) {
141
            $viewToInclude = $packageModuleView;
142
        } elseif (view()->exists($packageDefaultView)) {
143
            $viewToInclude = $packageDefaultView;
144
        } elseif (view()->exists($uccelloModuleView)) {
145
            $viewToInclude = $uccelloModuleView;
146
        } elseif (view()->exists($uccelloDefaultView)) {
147
            $viewToInclude = $uccelloDefaultView;
148
        } elseif (!is_null($fallbackView)) {
149
            $viewToInclude = $fallbackView;
150
        }
151
152
        return $viewToInclude;
153
    }
154
155
    /**
156
     * Makes route automaticaly and add module parameter.
157
     *
158
     * @param array|string $name
159
     * @param Domain|string|null $domain
160
     * @param Module|string|null $module
161
     * @param mixed $parameters
162
     * @param boolean $absolute
163
     * @return string
164
     */
165
    public function route($name, $domain = null, $module = null, $parameters = [ ], $absolute = true) : string
166
    {
167
        if (is_a($domain, Domain::class)) {
168
            $domain = $domain->slug;
0 ignored issues
show
Bug introduced by
The property slug does not seem to exist on Uccello\Core\Models\Domain.
Loading history...
169
        }
170
171
        if (is_a($module, Module::class)) {
172
            $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...
173
        }
174
175
        // Get route uri to check if domain and module parameters are needed
176
        $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

176
        $routeUri = \Route::getRoutes()->getByName(/** @scrutinizer ignore-type */ $name)->uri ?? null;
Loading history...
177
178
        // Add domain to route if we use multi domains and if the parameter is needed
179
        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...
180
            $parameters[ 'domain' ] = $domain;
181
        }
182
183
        // Add module to route if the parameter is needed
184
        if (!is_null($module) && preg_match('`{module}`', $routeUri)) {
185
            $parameters[ 'module' ] = $module;
186
        }
187
188
        return route($name, $parameters, $absolute);
189
    }
190
191
    /**
192
     * Returns the list of capabilities.
193
     *
194
     * @return \Illuminate\Database\Eloquent\Collection
195
     *
196
     * @see Uccello\Core\Models\Permission
197
     */
198
    public function getCapabilities(): Collection
199
    {
200
        return Capability::all();
201
    }
202
203
    /**
204
     * Get a domain instance by slug or id
205
     *
206
     * @param string|int $slugOrId
207
     * @return Domain|null
208
     */
209
    public function getDomain($slugOrId): ?Domain
210
    {
211
        if (is_numeric($slugOrId)) {
212
            return Domain::find($slugOrId);
213
        } else {
214
            return Domain::where('slug', (string)$slugOrId)->first();
215
        }
216
    }
217
218
    /**
219
     * Get a module instance by name or id
220
     *
221
     * @param string|int $nameOrId
222
     * @return Module|null
223
     */
224
    public function getModule($nameOrId): ?Module
225
    {
226
        if (is_numeric($nameOrId)) {
227
            return Module::find($nameOrId);
228
        } else {
229
            return Module::where('name', (string)$nameOrId)->first();
230
        }
231
    }
232
233
    /**
234
     * Get an Uitype instance by name or id
235
     *
236
     * @param string|int $nameOrId
237
     * @return Uitype|null
238
     */
239
    public function getUitype($nameOrId): ?Uitype
240
    {
241
        if (is_numeric($nameOrId)) {
242
            return Uitype::find($nameOrId);
243
        } else {
244
            return Uitype::where('name', (string)$nameOrId)->first();
245
        }
246
    }
247
248
    /**
249
     * Get a display type instance by name or id
250
     *
251
     * @param string|int $nameOrId
252
     * @return Uitype|null
253
     */
254
    public function getDisplaytype($nameOrId): ?Displaytype
255
    {
256
        if (is_numeric($nameOrId)) {
257
            return Displaytype::find($nameOrId);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Uccello\Core\Mode...aytype::find($nameOrId) also could return the type Uccello\Core\Models\Displaytype which is incompatible with the documented return type Uccello\Core\Models\Uitype|null.
Loading history...
258
        } else {
259
            return Displaytype::where('name', (string)$nameOrId)->first();
0 ignored issues
show
Bug Best Practice introduced by
The expression return Uccello\Core\Mode...ing)$nameOrId)->first() also could return the type Uccello\Core\Models\Displaytype which is incompatible with the documented return type Uccello\Core\Models\Uitype|null.
Loading history...
260
        }
261
    }
262
263
    /**
264
     * Get a capability instance by name or id
265
     *
266
     * @param string|int $nameOrId
267
     * @return Uitype|null
268
     */
269
    public function getCapability($nameOrId): ?Capability
270
    {
271
        if (is_numeric($nameOrId)) {
272
            return Capability::find($nameOrId);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Uccello\Core\Mode...bility::find($nameOrId) also could return the type Uccello\Core\Models\Capability which is incompatible with the documented return type Uccello\Core\Models\Uitype|null.
Loading history...
273
        } else {
274
            return Capability::where('name', (string)$nameOrId)->first();
0 ignored issues
show
Bug Best Practice introduced by
The expression return Uccello\Core\Mode...ing)$nameOrId)->first() also could return the type Uccello\Core\Models\Capability which is incompatible with the documented return type Uccello\Core\Models\Uitype|null.
Loading history...
275
        }
276
    }
277
278
    /**
279
     * Returns all domains without parents
280
     *
281
     * @return Collection
282
     */
283
    public function getRootDomains(): Collection
284
    {
285
        return Domain::whereNull('parent_id')->get();
286
    }
287
288
    /**
289
     * Get last domain visited by the connected user, or the first one available
290
     *
291
     * @return Domain|null
292
     */
293
    public function getLastOrDefaultDomain(): ?Domain
294
    {
295
        $domain = Auth::user()->lastDomain ?? 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...
296
297
        if (!$domain) {
298
            $domain = $this->getRootDomains()[ 0 ];
299
        }
300
301
        return $domain;
302
    }
303
304
    /**
305
     * Retrieve columns to display in a datatable table
306
     *
307
     * @param Module $module
308
     * @param integer $filterId
309
     * @param string $type
310
     * @return array
311
     */
312
    public function getDatatableColumns(Module $module, $filterId=null, $type='list'): array
313
    {
314
        $columns = [ ];
315
316
        // Get default filter
317
        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...
318
            $filter = Filter::find($filterId);
319
        } else {
320
            $filter = Filter::where('module_id', $module->id)
321
                ->where('type', $type)
322
                ->first();
323
324
            // If there is not result, try with type = list
325
            if (empty($filter) && $type !== 'list') {
326
                $filter = Filter::where('module_id', $module->id)
327
                ->where('type', 'list')
328
                ->first();
329
            }
330
        }
331
332
        if (empty($filter)) {
333
            return [ ];
334
        }
335
336
        // Get all fields
337
        $fields = $module->fields;
338
339
        foreach ($fields as $field) {
340
            // If the field is not listable, continue
341
            if (!$field->isListable()) {
342
                continue;
343
            }
344
345
            // Add the field as a new column
346
            $columns[ ] = [
347
                'name' => $field->name,
348
                'db_column' => $field->column,
349
                'uitype' => $field->uitype->name,
350
                'package' => $field->uitype->package,
351
                'data' => $field->data,
352
                'visible' => in_array($field->name, $filter->columns)
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...
353
            ];
354
        }
355
356
        return $columns;
357
    }
358
359
    /**
360
     * Returns a record attribute value.
361
     * It is able to follow a complex path according to models definition (e.g. 'domain.parent.name')
362
     *
363
     * @param Object $record
364
     * @param string $attribute
365
     * @return string|Object|Array|null
366
     */
367
    public function getRecordAttribute($record, string $attribute) {
368
369
        $attributeParts = explode('.', $attribute);
370
371
        if (count($attributeParts) > 0) {
372
            $value = $record;
373
374
            foreach ($attributeParts as $part) {
375
                // Get attribute value if exists
376
                if (isset($value->{$part})) {
377
                    $value = $value->{$part};
378
                }
379
                // If property does not exist return an empty value
380
                else {
381
                    $value = null;
382
                    break;
383
                }
384
            }
385
        } else {
386
            $value = $record->{$attribute};
387
        }
388
389
        return $value;
390
    }
391
}