Passed
Pull Request — master (#51)
by
unknown
20:10 queued 10s
created

ListController::buildContentQuery()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 36
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 16
c 3
b 1
f 0
dl 0
loc 36
rs 8.4444
cc 8
nc 7
nop 0
1
<?php
2
3
namespace Uccello\Core\Http\Controllers\Core;
4
5
use Schema;
0 ignored issues
show
Bug introduced by
The type Schema 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...
6
use Illuminate\Http\Request;
0 ignored issues
show
Bug introduced by
The type Illuminate\Http\Request 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...
7
use Spatie\Searchable\Search;
0 ignored issues
show
Bug introduced by
The type Spatie\Searchable\Search 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...
8
use Uccello\Core\Models\Domain;
9
use Uccello\Core\Models\Module;
10
use Uccello\Core\Facades\Uccello;
11
use Uccello\Core\Models\Relatedlist;
12
use Uccello\Core\Models\Filter;
13
14
class ListController extends Controller
15
{
16
    protected $viewName = 'list.main';
17
18
    /**
19
     * Check user permissions
20
     */
21
    protected function checkPermissions()
22
    {
23
        $this->middleware('uccello.permissions:retrieve');
24
    }
25
26
    /**
27
     * @inheritDoc
28
     */
29
    public function process(?Domain $domain, Module $module, Request $request)
30
    {
31
        // Pre-process
32
        $this->preProcess($domain, $module, $request);
33
34
        // Selected filter
35
        if ($request->input('filter')) {
36
            $selectedFilterId = $request->input('filter');
37
            $selectedFilter = Filter::find($selectedFilterId);
38
        }
39
40
        if (empty($selectedFilter)) { // For example if the given filter does not exist
41
            $selectedFilter = $module->filters()->where('type', 'list')->first();
42
            $selectedFilterId = $selectedFilter->id;
43
        }
44
45
        // Add search conditions from request
46
        $selectedFilter = $this->addSearchConditions($selectedFilter);
47
48
        // Get datatable columns
49
        $datatableColumns = Uccello::getDatatableColumns($module, $selectedFilterId);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $selectedFilterId does not seem to be defined for all execution paths leading up to this point.
Loading history...
50
51
        // Get filters
52
        $filters = Filter::where('module_id', $module->id)  // Module
53
            ->where('type', 'list')                         // Type (list)
54
            ->where(function ($query) use($domain) {        // Domain
55
                $query->whereNull('domain_id')
56
                    ->orWhere('domain_id', $domain->getKey());
57
            })
58
            ->where(function ($query) {                     // User
59
                $query->where('is_public', true)
60
                    ->orWhere(function ($query) {
61
                        $query->where('is_public', false)
62
                            ->where('user_id', '=', auth()->id());
0 ignored issues
show
Bug introduced by
The function auth was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

62
                            ->where('user_id', '=', /** @scrutinizer ignore-call */ auth()->id());
Loading history...
63
                    })
64
                    ->orWhere(function ($query) {
65
                        $query->where('is_public', false)
66
                            ->whereNull('user_id');
67
                    });
68
            })
69
            ->orderBy('order')
70
            ->get();
71
72
        // Order
73
        $filterOrder = (array) $selectedFilter->order;
74
75
        // See descendants
76
        $seeDescendants = request()->session()->get('descendants');
0 ignored issues
show
Bug introduced by
The function request was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

76
        $seeDescendants = /** @scrutinizer ignore-call */ request()->session()->get('descendants');
Loading history...
77
78
        // Use soft deleting
79
        $usesSoftDeleting = $this->isModuleUsingSoftDeleting();
80
81
        // Check if we want to display trash data
82
        $displayTrash = $this->isDisplayingTrash();
83
84
        return $this->autoView(compact(
85
            'datatableColumns',
86
            'filters',
87
            'selectedFilter',
88
            'filterOrder',
89
            'seeDescendants',
90
            'usesSoftDeleting',
91
            'displayTrash'
92
        ));
93
    }
94
95
    /**
96
     * Display a listing of the resources.
97
     * The result is formated differently if it is a classic query or one requested by datatable.
98
     * Filter on domain if domain_id column exists.
99
     * @param  \Uccello\Core\Models\Domain|null $domain
100
     * @param  \Uccello\Core\Models\Module $module
101
     * @param  \Illuminate\Http\Request $request
102
     * @return \Illuminate\Http\Response
0 ignored issues
show
Bug introduced by
The type Illuminate\Http\Response 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...
103
     */
104
    public function processForContent(?Domain $domain, Module $module, Request $request)
105
    {
106
        $length = (int)$request->get('length') ?? env('UCCELLO_ITEMS_PER_PAGE', 15);
0 ignored issues
show
Bug introduced by
The function env was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

106
        $length = (int)$request->get('length') ?? /** @scrutinizer ignore-call */ env('UCCELLO_ITEMS_PER_PAGE', 15);
Loading history...
107
108
        $recordId = $request->get('id');
109
        $relatedListId = $request->get('relatedlist');
110
        $action = $request->get('action');
111
        $relatedModule = null;
112
113
        if ($request->has('descendants') && $request->get('descendants') !== $request->session()->get('descendants')) {
114
            $request->session()->put('descendants', $request->get('descendants'));
115
        }
116
117
        // Pre-process
118
        $this->preProcess($domain, $module, $request, false);
119
120
        // Get model model class
121
        $modelClass = $module->model_class;
122
123
        // Check if the class exists
124
        if (!class_exists($modelClass)) {
125
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type Illuminate\Http\Response.
Loading history...
126
        }
127
128
        // Build query
129
        $query = $this->buildContentQuery();
130
131
        // Limit the number maximum of items per page
132
        $maxItemsPerPage = env('UCCELLO_MAX_ITEMS_PER_PAGE', 100);
133
        if ($length > $maxItemsPerPage) {
134
            $length = $maxItemsPerPage;
135
        }
136
137
        // If the query is for a related list, add conditions
138
        if ($relatedListId && $action !== 'select') {
139
            // Get related list
140
            $relatedList = Relatedlist::find($relatedListId);
141
142
            if ($relatedList && $relatedList->method) {
143
                // Related list method
144
                $method = $relatedList->method;
145
146
                // Update query
147
                $model = new $modelClass;
148
                $records = $model->$method($relatedList, $recordId, $query)->paginate($length);
149
            }
150
        } elseif ($relatedListId && $action === 'select') {
151
            // Get related list
152
            $relatedList = Relatedlist::find($relatedListId);
153
154
            if ($relatedList && $relatedList->method) {
155
                // Related list method
156
                $method = $relatedList->method;
157
                $recordIdsMethod = $method . 'RecordIds';
158
159
                // Get related records ids
160
                $model = new $modelClass;
161
                $filteredRecordIds = $model->$recordIdsMethod($relatedList, $recordId);
162
163
                // Add the record id itself to be filtered
164
                if ($relatedList->module_id === $relatedList->related_module_id && !empty($recordId) && !$filteredRecordIds->contains($recordId)) {
165
                    $filteredRecordIds[] = (int)$recordId;
166
                }
167
168
                if ($relatedList->module_id) {
169
                    $relatedModule = ucmodule($relatedList->module_id);
170
                }
171
172
                // Make the quer
173
                $records = $query->whereNotIn($model->getKeyName(), $filteredRecordIds)->paginate($length);
174
            }
175
        } else {
176
            // Paginate results
177
            $records = $query->paginate($length);
178
        }
179
180
        $records->getCollection()->transform(function ($record) use ($domain, $module, $relatedModule) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $records does not seem to be defined for all execution paths leading up to this point.
Loading history...
181
            foreach ($module->fields as $field) {
182
                // If a special template exists, use it. Else use the generic template
183
                $uitype = uitype($field->uitype_id);
184
                $uitypeViewName = sprintf('uitypes.list.%s', $uitype->name);
185
                $uitypeFallbackView = 'uccello::modules.default.uitypes.list.text';
186
                $uitypeViewToInclude = uccello()->view($module->package, $module, $uitypeViewName, $uitypeFallbackView);
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...
187
                $record->{$field->name.'_html'} = view()->make($uitypeViewToInclude, compact('domain', 'module', 'record', 'field'))->render();
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

187
                $record->{$field->name.'_html'} = /** @scrutinizer ignore-call */ view()->make($uitypeViewToInclude, compact('domain', 'module', 'record', 'field'))->render();
Loading history...
188
            }
189
190
            // Add primary key name and value
191
            $record->__primaryKey = $record->getKey();
192
            $record->__primaryKeyName = $record->getKeyName();
193
194
            if ($relatedModule) {
195
                $moduleName = str_replace('-', '_', $relatedModule->name);
196
                if ($record->$moduleName) {
197
                    $record->__relatedEntityName = $record->$moduleName->recordLabel ?? null;
198
                }
199
            }
200
201
            return $record;
202
        });
203
204
        return $records;
205
    }
206
207
    /**
208
     * Autocomplete a listing of the resources.
209
     * The result is formated differently if it is a classic query or one requested by datatable.
210
     * Filter on domain if domain_id column exists.
211
     * @param  \Uccello\Core\Models\Domain|null $domain
212
     * @param  \Uccello\Core\Models\Module $module
213
     * @param  \Illuminate\Http\Request $request
214
     * @return \Illuminate\Http\Response
215
     */
216
    public function processForAutocomplete(?Domain $domain, Module $module, Request $request)
217
    {
218
        // If we don't use multi domains, find the first one
219
        if (!uccello()->useMultiDomains()) {
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...
220
            $domain = Domain::first();
0 ignored issues
show
Unused Code introduced by
The assignment to $domain is dead and can be removed.
Loading history...
221
        }
222
223
        // Query
224
        $q = $request->get('q');
225
        // Model class
226
        $modelClass = $module->model_class;
227
228
        $results = collect();
0 ignored issues
show
Bug introduced by
The function collect was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

228
        $results = /** @scrutinizer ignore-call */ collect();
Loading history...
229
        if (method_exists($modelClass, 'getSearchResult') && property_exists($modelClass, 'searchableColumns')) {
230
            $searchResults = new Search();
231
            $searchResults->registerModel($modelClass, (array) (new $modelClass)->searchableColumns);
232
            $results = $searchResults->search($q)->take(config('uccello.max_results.autocomplete', 10));
0 ignored issues
show
Bug introduced by
The function config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

232
            $results = $searchResults->search($q)->take(/** @scrutinizer ignore-call */ config('uccello.max_results.autocomplete', 10));
Loading history...
233
        }
234
235
        return $results;
236
    }
237
238
    /**
239
     * Save list filter into database
240
     *
241
     * @param \Uccello\Core\Models\Domain|null $domain
242
     * @param \Uccello\Core\Models\Module $module
243
     * @param \Illuminate\Http\Request $request
244
     * @return \Illuminate\Http\Response
245
     */
246
    public function saveFilter(?Domain $domain, Module $module, Request $request)
247
    {
248
        $saveOrder = $request->input('save_order');
249
        $savePageLength = $request->input('save_page_length');
250
251
        // Optional data
252
        $data = [];
253
        if ($savePageLength) {
254
            $data["length"] = $request->input('page_length');
255
        }
256
257
        $filter = Filter::firstOrNew([
258
            'domain_id' => $domain->id,
259
            'module_id' => $module->id,
260
            'user_id' => auth()->id(),
0 ignored issues
show
Bug introduced by
The function auth was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

260
            'user_id' => /** @scrutinizer ignore-call */ auth()->id(),
Loading history...
261
            'name' => $request->input('name'),
262
            'type' => $request->input('type')
263
        ]);
264
        $filter->columns = $request->input('columns');
265
        $filter->conditions = $request->input('conditions') ?? null;
266
        $filter->order = $saveOrder ? $request->input('order') : null;
267
        $filter->is_default = $request->input('default');
268
        $filter->is_public = $request->input('public');
269
        $filter->data = !empty($data) ? $data : null;
270
        $filter->save();
271
272
        return $filter;
273
    }
274
275
    /**
276
     * Retrieve a filter by its id and delete it
277
     *
278
     * @param \Uccello\Core\Models\Domain|null $domain
279
     * @param \Uccello\Core\Models\Module $module
280
     * @param \Illuminate\Http\Request $request
281
     * @return \Illuminate\Http\Response
282
     */
283
    public function deleteFilter(?Domain $domain, Module $module, Request $request)
284
    {
285
        // Retrieve filter by id
286
        $filterId = $request->input('id');
287
        $filter = Filter::find($filterId);
288
289
        if ($filter) {
290
            if ($filter->readOnly) {
291
                // Response
292
                $success = false;
293
                $message = uctrans('error.filter.read_only', $module);
294
            } else {
295
                // Delete
296
                $filter->delete();
297
298
                // Response
299
                $success = true;
300
                $message = uctrans('success.filter.deleted', $module);
301
            }
302
        } else {
303
            // Response
304
            $success = false;
305
            $message = uctrans('error.filter.not_found', $module);
306
        }
307
308
        return [
309
            'success' => $success,
310
            'message' => $message
311
        ];
312
    }
313
314
    /**
315
     * Check if the model class link to the module is using soft deleting.
316
     *
317
     * @return boolean
318
     */
319
    protected function isModuleUsingSoftDeleting()
320
    {
321
        $modelClass = $this->module->model_class;
322
        $model = new $modelClass;
323
324
        return method_exists($model, 'getDeletedAtColumn');
325
    }
326
327
    /**
328
     * Check if we want to display trash data
329
     *
330
     * @return boolean
331
     */
332
    protected function isDisplayingTrash()
333
    {
334
        return $this->isModuleUsingSoftDeleting() && $this->request->get('filter') === 'trash';
335
    }
336
337
    /**
338
     * Add to a filter all search conditions from defined in request
339
     *
340
     * @param \Uccello\Core\Models\Filter $filter
341
     * @return \Uccello\Core\Models\Filter
342
     */
343
    protected function addSearchConditions($filter)
344
    {
345
        if ($this->request->has('search')) {
346
            $conditions = [];
347
            foreach ((array) $this->request->search as $fieldName => $value) {
348
                $conditions[$fieldName] = $value;
349
            }
350
351
            if ($conditions) {
352
                $filter->conditions = [
353
                    'search' => $conditions
354
                ];
355
            }
356
        }
357
358
        return $filter;
359
    }
360
361
    /**
362
     * Build query for retrieving content
363
     *
364
     * @return \Illuminate\Database\Eloquent\Builder;
0 ignored issues
show
Bug introduced by
The type Illuminate\Database\Eloquent\Builder 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...
365
     */
366
    protected function buildContentQuery()
367
    {
368
        $filter = [
369
            'order' => $this->request->get('order'),
370
            'columns' => $this->request->get('columns'),
371
        ];
372
373
        // Get model model class
374
        $modelClass = $this->module->model_class;
375
376
        // Check if the class exists
377
        if (!class_exists($modelClass) || !method_exists($modelClass, 'scopeInDomain')) {
378
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
379
        }
380
381
        //Filter with additionnal rules (uitype entity for exemple)
382
        if ($this->request->has('additional_rules') && $this->request->get('additional_rules')) {
383
            $rules = json_decode($this->request->get('additional_rules'));
384
385
            if ($rules) {
386
                foreach ($rules as $column => $rule) {
387
                    $filter['columns'][$column]['search'] = $rule;
388
                }
389
            }
390
        }
391
392
        // Filter on domain if column exists
393
        $query = $modelClass::inDomain($this->domain, $this->request->session()->get('descendants'))
394
                            ->filterBy($filter);
395
396
        // Display trash if filter is selected
397
        if ($this->isDisplayingTrash()) {
398
            $query = $query->onlyTrashed();
399
        }
400
401
        return $query;
402
    }
403
}
404