GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 414922...a9bc98 )
by butschster
12:35
created

DisplayDatatablesAsync::applyOrders()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 12
nc 4
nop 1
dl 0
loc 18
rs 8.8571
c 1
b 0
f 0
1
<?php
2
3
namespace SleepingOwl\Admin\Display;
4
5
use Request;
6
use Illuminate\Routing\Router;
7
use Illuminate\Support\Collection;
8
use Illuminate\Database\Eloquent\Builder;
9
use SleepingOwl\Admin\Display\Column\Link;
10
use SleepingOwl\Admin\Display\Column\Text;
11
use SleepingOwl\Admin\Display\Column\Email;
12
use Illuminate\Http\Request as InlineRequest;
13
use SleepingOwl\Admin\Display\Column\Control;
14
use SleepingOwl\Admin\Contracts\WithRoutesInterface;
15
use SleepingOwl\Admin\Contracts\ModelConfigurationInterface;
16
use SleepingOwl\Admin\Contracts\Display\ColumnEditableInterface;
17
18
class DisplayDatatablesAsync extends DisplayDatatables implements WithRoutesInterface
19
{
20
    /**
21
     * Register display routes.
22
     *
23
     * @param Router $router
24
     */
25
    public static function registerRoutes(Router $router)
26
    {
27
        $router->get('{adminModel}/async/{adminDisplayName?}', [
28
            'as' => 'admin.model.async',
29
            function (ModelConfigurationInterface $model, $name = null) {
30
                $display = $model->fireDisplay();
31
                if ($display instanceof DisplayTabbed) {
32
                    $display = static::findDatatablesAsyncByName($display, $name);
33
                }
34
35
                if ($display instanceof DisplayDatatablesAsync) {
36
                    return $display->renderAsync();
37
                }
38
39
                abort(404);
40
            },
41
        ]);
42
        $router->post('{adminModel}/async/{adminDisplayName?}', [
43
            'as' => 'admin.model.async.inline',
44
            function (ModelConfigurationInterface $model, InlineRequest $request) {
45
                $field = $request->input('name');
46
                $value = $request->input('value');
47
                $id = $request->input('pk');
48
49
                $display = $model->fireDisplay();
50
51
                /** @var ColumnEditableInterface|null $column */
52
                $column = $display->getColumns()->all()->filter(function ($column) use ($field) {
53
                    return ($column instanceof ColumnEditableInterface) and $field == $column->getName();
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
54
                })->first();
55
56
                if (is_null($column)) {
57
                    abort(404);
58
                }
59
60
                $model->saveColumn($column, $value, $id);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SleepingOwl\Admin\Contra...lConfigurationInterface as the method saveColumn() does only exist in the following implementations of said interface: SleepingOwl\Admin\Model\ModelConfiguration.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
61
62
                if ($display instanceof DisplayDatatablesAsync) {
63
                    return $display->renderAsync();
64
                }
65
            },
66
        ]);
67
    }
68
69
    /**
70
     * Find DisplayDatatablesAsync in tabbed display by name.
71
     *
72
     * @param DisplayTabbed $display
73
     * @param string|null $name
74
     *
75
     * @return DisplayDatatablesAsync|null
76
     */
77
    protected static function findDatatablesAsyncByName(DisplayTabbed $display, $name)
78
    {
79
        $tabs = $display->getTabs();
80
        foreach ($tabs as $tab) {
81
            $content = $tab->getContent();
82
            if ($content instanceof self && $content->getName() === $name) {
83
                return $content;
84
            }
85
        }
86
    }
87
88
    /**
89
     * @var string
90
     */
91
    protected $name;
92
93
    /**
94
     * @param string|null $name
95
     */
96
    protected $distinct;
97
98
    /**
99
     * @var array
100
     */
101
    protected $searchableColumns = [
102
        Text::class,
103
        Link::class,
104
        Email::class,
105
    ];
106
107
    /**
108
     * DisplayDatatablesAsync constructor.
109
     *
110
     * @param string|null $name
111
     * @param string|null $distinct
112
     */
113
    public function __construct($name = null, $distinct = null)
114
    {
115
        parent::__construct();
116
117
        $this->setName($name);
118
        $this->setDistinct($distinct);
119
120
        $this->getColumns()->setView('display.extensions.columns_async');
121
    }
122
123
    /**
124
     * Initialize display.
125
     */
126
    public function initialize()
127
    {
128
        parent::initialize();
129
130
        $attributes = Request::all();
131
        array_unshift($attributes, $this->getName());
132
        array_unshift($attributes, $this->getModelConfiguration()->getAlias());
133
134
        $this->setHtmlAttribute('data-url', route('admin.model.async', $attributes));
135
    }
136
137
    /**
138
     * @return string
139
     */
140
    public function getName()
141
    {
142
        return $this->name;
143
    }
144
145
    /**
146
     * @param string $name
147
     *
148
     * @return $this
149
     */
150
    public function setName($name)
151
    {
152
        $this->name = $name;
153
154
        return $this;
155
    }
156
157
    /**
158
     * @return mixed
159
     */
160
    public function getDistinct()
161
    {
162
        return $this->distinct;
163
    }
164
165
    /**
166
     * @param mixed $distinct
167
     *
168
     * @return $this
169
     */
170
    public function setDistinct($distinct)
171
    {
172
        $this->distinct = $distinct;
173
174
        return $this;
175
    }
176
177
    /**
178
     * Render async request.
179
     * @return array
180
     */
181
    public function renderAsync()
182
    {
183
        $query = $this->getRepository()->getQuery();
184
        $totalCount = $query->count();
185
        $filteredCount = 0;
186
187
        if (! is_null($this->distinct)) {
188
            $filteredCount = $query->distinct()->count($this->getDistinct());
189
        }
190
191
        $this->modifyQuery($query);
192
        $this->applySearch($query);
193
        $this->applyColumnSearch($query);
194
195
        if (is_null($this->distinct)) {
196
            $filteredCount = $query->count();
197
        }
198
199
        $this->applyOrders($query);
200
        $this->applyOffset($query);
201
        $collection = $query->get();
202
203
        return $this->prepareDatatablesStructure($collection, $totalCount, $filteredCount);
0 ignored issues
show
Bug introduced by
It seems like $collection defined by $query->get() on line 201 can also be of type array<integer,object<Ill...base\Eloquent\Builder>>; however, SleepingOwl\Admin\Displa...reDatatablesStructure() does only seem to accept object<Illuminate\Support\Collection>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
204
    }
205
206
    /**
207
     * Apply offset and limit to the query.
208
     *
209
     * @param $query
210
     */
211
    protected function applyOffset($query)
212
    {
213
        $offset = Request::input('start', 0);
214
        $limit = Request::input('length', 10);
215
216
        if ($limit == -1) {
217
            return;
218
        }
219
220
        $query->offset($offset)->limit($limit);
221
    }
222
223
    /**
224
     * Apply search to the query.
225
     *
226
     * @param Builder $query
227
     */
228
    protected function applySearch(Builder $query)
229
    {
230
        $search = Request::input('search.value');
231
        if (empty($search)) {
232
            return;
233
        }
234
235
        $query->where(function ($query) use ($search) {
236
            $columns = $this->getColumns()->all();
237
            foreach ($columns as $column) {
238
                if (in_array(get_class($column), $this->searchableColumns)) {
239
                    $name = $column->getName();
240
                    if ($this->repository->hasColumn($name)) {
241
                        $query->orWhere($name, 'like', '%'.$search.'%');
242
                    }
243
                }
244
            }
245
        });
246
    }
247
248
    /**
249
     * @param Builder $query
250
     */
251
    protected function applyColumnSearch(Builder $query)
252
    {
253
        $queryColumns = Request::input('columns', []);
254
255
        foreach ($queryColumns as $index => $queryColumn) {
0 ignored issues
show
Bug introduced by
The expression $queryColumns of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
256
            $search = array_get($queryColumn, 'search.value');
257
            $fullSearch = array_get($queryColumn, 'search');
258
            $column = $this->getColumns()->all()->get($index);
259
            $columnFilter = array_get($this->getColumnFilters()->all(), $index);
260
261
            if (! is_null($columnFilter) && ! is_null($column)) {
262
                $columnFilter->apply($this->repository, $column, $query, $search, $fullSearch);
263
            }
264
        }
265
    }
266
267
    /**
268
     * Convert collection to the datatables structure.
269
     *
270
     * @param array|Collection $collection
271
     * @param int $totalCount
272
     * @param int $filteredCount
273
     *
274
     * @return array
275
     */
276
    protected function prepareDatatablesStructure(Collection $collection, $totalCount, $filteredCount)
277
    {
278
        $columns = $this->getColumns();
279
280
        $result = [];
281
        $result['draw'] = Request::input('draw', 0);
282
        $result['recordsTotal'] = $totalCount;
283
        $result['recordsFiltered'] = $filteredCount;
284
        $result['data'] = [];
285
286
        foreach ($collection as $instance) {
287
            $_row = [];
288
289
            foreach ($columns->all() as $column) {
290
                $column->setModel($instance);
291
292
                if ($column instanceof Control) {
293
                    $column->initialize();
294
                }
295
296
                $_row[] = (string) $column;
297
            }
298
299
            $result['data'][] = $_row;
300
        }
301
302
        return $result;
303
    }
304
}
305