Passed
Push — master ( 5cd307...f696e4 )
by Jonathan
24:08
created

ListController::addSearchConditions()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 16
rs 10
cc 4
nc 3
nop 1
1
<?php
2
3
namespace Uccello\Core\Http\Controllers\Core;
4
5
use Schema;
6
use Illuminate\Http\Request;
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...
Bug introduced by
The method getDatatableColumns() does not exist on Uccello\Core\Facades\Uccello. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

49
        /** @scrutinizer ignore-call */ 
50
        $datatableColumns = Uccello::getDatatableColumns($module, $selectedFilterId);
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());
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;
0 ignored issues
show
Bug introduced by
The property order 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...
74
75
        // See descendants
76
        $seeDescendants = request()->session()->get('descendants');
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
103
     */
104
    public function processForContent(?Domain $domain, Module $module, Request $request)
105
    {
106
        $length = (int)$request->get('length') ?? env('UCCELLO_ITEMS_PER_PAGE', 15);
107
108
        $recordId = $request->get('id');
109
        $relatedListId = $request->get('relatedlist');
110
        $action = $request->get('action');
111
112
        if ($request->has('descendants') && $request->get('descendants') !== $request->session()->get('descendants')) {
113
            $request->session()->put('descendants', $request->get('descendants'));
114
        }
115
116
        // Pre-process
117
        $this->preProcess($domain, $module, $request, false);
118
119
        // Get model model class
120
        $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...
121
122
        // Check if the class exists
123
        if (!class_exists($modelClass)) {
124
            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...
125
        }
126
127
        // Build query
128
        $query = $this->buildContentQuery();
129
130
        // Limit the number maximum of items per page
131
        $maxItemsPerPage = env('UCCELLO_MAX_ITEMS_PER_PAGE', 100);
132
        if ($length > $maxItemsPerPage) {
133
            $length = $maxItemsPerPage;
134
        }
135
136
        // If the query is for a related list, add conditions
137
        if ($relatedListId && $action !== 'select') {
138
            // Get related list
139
            $relatedList = Relatedlist::find($relatedListId);
140
141
            if ($relatedList && $relatedList->method) {
0 ignored issues
show
Bug introduced by
The property method does not seem to exist on Uccello\Core\Models\Relatedlist. 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...
142
                // Related list method
143
                $method = $relatedList->method;
144
145
                // Update query
146
                $model = new $modelClass;
147
                $records = $model->$method($relatedList, $recordId, $query)->paginate($length);
148
            }
149
        } elseif ($relatedListId && $action === 'select') {
150
            // Get related list
151
            $relatedList = Relatedlist::find($relatedListId);
152
153
            if ($relatedList && $relatedList->method) {
154
                // Related list method
155
                $method = $relatedList->method;
156
                $recordIdsMethod = $method . 'RecordIds';
157
158
                // Get related records ids
159
                $model = new $modelClass;
160
                $filteredRecordIds = $model->$recordIdsMethod($relatedList, $recordId);
161
162
                // Add the record id itself to be filtered
163
                if ($relatedList->module_id === $relatedList->related_module_id && !empty($recordId) && !$filteredRecordIds->contains($recordId)) {
0 ignored issues
show
Bug introduced by
The property related_module_id does not exist on Uccello\Core\Models\Relatedlist. Did you mean module?
Loading history...
Bug introduced by
The property module_id does not exist on Uccello\Core\Models\Relatedlist. Did you mean module?
Loading history...
164
                    $filteredRecordIds[] = (int)$recordId;
165
                }
166
167
                // Make the quer
168
                $records = $query->whereNotIn($model->getKeyName(), $filteredRecordIds)->paginate($length);
169
            }
170
        } else {
171
            // Paginate results
172
            $records = $query->paginate($length);
173
        }
174
175
        $records->getCollection()->transform(function ($record) use ($domain, $module) {
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...
176
            foreach ($module->fields as $field) {
177
                // If a special template exists, use it. Else use the generic template
178
                $uitype = uitype($field->uitype_id);
179
                $uitypeViewName = sprintf('uitypes.list.%s', $uitype->name);
180
                $uitypeFallbackView = 'uccello::modules.default.uitypes.list.text';
181
                $uitypeViewToInclude = uccello()->view($uitype->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...
182
                $record->{$field->name.'_html'} = view()->make($uitypeViewToInclude, compact('domain', 'module', 'record', 'field'))->render();
183
            }
184
185
            // Add primary key name and value
186
            $record->__primaryKey = $record->getKey();
187
            $record->__primaryKeyName = $record->getKeyName();
188
189
            return $record;
190
        });
191
192
        return $records;
193
    }
194
195
    /**
196
     * Autocomplete a listing of the resources.
197
     * The result is formated differently if it is a classic query or one requested by datatable.
198
     * Filter on domain if domain_id column exists.
199
     * @param  \Uccello\Core\Models\Domain|null $domain
200
     * @param  \Uccello\Core\Models\Module $module
201
     * @param  \Illuminate\Http\Request $request
202
     * @return \Illuminate\Http\Response
203
     */
204
    public function processForAutocomplete(?Domain $domain, Module $module, Request $request)
205
    {
206
        // If we don't use multi domains, find the first one
207
        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...
208
            $domain = Domain::first();
0 ignored issues
show
Unused Code introduced by
The assignment to $domain is dead and can be removed.
Loading history...
209
        }
210
211
        // Query
212
        $q = $request->get('q');
213
        // Model class
214
        $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...
215
216
        $results = collect();
217
        if (method_exists($modelClass, 'getSearchResult') && property_exists($modelClass, 'searchableColumns')) {
218
            $searchResults = new Search();
219
            $searchResults->registerModel($modelClass, (array) (new $modelClass)->searchableColumns);
220
            $results = $searchResults->search($q)->take(config('uccello.max_results.autocomplete', 10));
221
        }
222
223
        return $results;
224
    }
225
226
    /**
227
     * Save list filter into database
228
     *
229
     * @param \Uccello\Core\Models\Domain|null $domain
230
     * @param \Uccello\Core\Models\Module $module
231
     * @param \Illuminate\Http\Request $request
232
     * @return \Illuminate\Http\Response
233
     */
234
    public function saveFilter(?Domain $domain, Module $module, Request $request)
235
    {
236
        $saveOrder = $request->input('save_order');
237
        $savePageLength = $request->input('save_page_length');
238
239
        // Optional data
240
        $data = [];
241
        if ($savePageLength) {
242
            $data["length"] = $request->input('page_length');
243
        }
244
245
        $filter = Filter::firstOrNew([
246
            'domain_id' => $domain->id,
247
            'module_id' => $module->id,
248
            'user_id' => auth()->id(),
249
            'name' => $request->input('name'),
250
            'type' => $request->input('type')
251
        ]);
252
        $filter->columns = $request->input('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...
253
        $filter->conditions = $request->input('conditions') ?? null;
0 ignored issues
show
Bug introduced by
The property conditions 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...
254
        $filter->order = $saveOrder ? $request->input('order') : null;
0 ignored issues
show
Bug introduced by
The property order 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...
255
        $filter->is_default = $request->input('default');
0 ignored issues
show
Bug introduced by
The property is_default 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...
256
        $filter->is_public = $request->input('public');
0 ignored issues
show
Bug introduced by
The property is_public 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...
257
        $filter->data = !empty($data) ? $data : null;
0 ignored issues
show
Bug introduced by
The property data 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...
258
        $filter->save();
259
260
        return $filter;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $filter returns the type Uccello\Core\Models\Filter which is incompatible with the documented return type Illuminate\Http\Response.
Loading history...
261
    }
262
263
    /**
264
     * Retrieve a filter by its id and delete it
265
     *
266
     * @param \Uccello\Core\Models\Domain|null $domain
267
     * @param \Uccello\Core\Models\Module $module
268
     * @param \Illuminate\Http\Request $request
269
     * @return \Illuminate\Http\Response
270
     */
271
    public function deleteFilter(?Domain $domain, Module $module, Request $request)
272
    {
273
        // Retrieve filter by id
274
        $filterId = $request->input('id');
275
        $filter = Filter::find($filterId);
276
277
        if ($filter) {
278
            if ($filter->readOnly) {
279
                // Response
280
                $success = false;
281
                $message = uctrans('error.filter.read_only', $module);
282
            } else {
283
                // Delete
284
                $filter->delete();
285
286
                // Response
287
                $success = true;
288
                $message = uctrans('success.filter.deleted', $module);
289
            }
290
        } else {
291
            // Response
292
            $success = false;
293
            $message = uctrans('error.filter.not_found', $module);
294
        }
295
296
        return [
297
            'success' => $success,
298
            'message' => $message
299
        ];
300
    }
301
302
    /**
303
     * Check if the model class link to the module is using soft deleting.
304
     *
305
     * @return boolean
306
     */
307
    protected function isModuleUsingSoftDeleting()
308
    {
309
        $modelClass = $this->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...
310
        $model = new $modelClass;
311
312
        return method_exists($model, 'getDeletedAtColumn');
313
    }
314
315
    /**
316
     * Check if we want to display trash data
317
     *
318
     * @return boolean
319
     */
320
    protected function isDisplayingTrash()
321
    {
322
        return $this->isModuleUsingSoftDeleting() && $this->request->get('filter') === 'trash';
323
    }
324
325
    /**
326
     * Add to a filter all search conditions from defined in request
327
     *
328
     * @param \Uccello\Core\Models\Filter $filter
329
     * @return \Uccello\Core\Models\Filter
330
     */
331
    protected function addSearchConditions($filter)
332
    {
333
        if ($this->request->has('search')) {
334
            $conditions = [];
335
            foreach ((array) $this->request->search as $fieldName => $value) {
336
                $conditions[$fieldName] = $value;
337
            }
338
339
            if ($conditions) {
340
                $filter->conditions = [
0 ignored issues
show
Bug introduced by
The property conditions 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...
341
                    'search' => $conditions
342
                ];
343
            }
344
        }
345
346
        return $filter;
347
    }
348
349
    /**
350
     * Build query for retrieving content
351
     *
352
     * @return \Illuminate\Database\Eloquent\Builder;
353
     */
354
    protected function buildContentQuery()
355
    {
356
        $filter = [
357
            'order' => $this->request->get('order'),
358
            'columns' => $this->request->get('columns'),
359
        ];
360
361
        // Get model model class
362
        $modelClass = $this->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...
363
364
        // Check if the class exists
365
        if (!class_exists($modelClass) || !method_exists($modelClass, 'scopeInDomain')) {
366
            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...
367
        }
368
369
        // Filter on domain if column exists
370
        $query = $modelClass::inDomain($this->domain, $this->request->session()->get('descendants'))
371
                            ->filterBy($filter);
372
373
        // Display trash if filter is selected
374
        if ($this->isDisplayingTrash()) {
375
            $query = $query->onlyTrashed();
376
        }
377
378
        return $query;
379
    }
380
}
381