Completed
Push — master ( fd5849...c99aaa )
by Renato
05:53
created

AbstractRepository::deleteWhere()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 1
dl 0
loc 19
ccs 11
cts 11
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace NwLaravel\Repositories\Eloquent;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Prettus\Repository\Eloquent\BaseRepository;
7
use NwLaravel\Repositories\RepositoryInterface;
8
use NwLaravel\Repositories\Criterias\InputCriteria;
9
use NwLaravel\Resultset\BuilderResultset;
10
use Prettus\Validator\Contracts\ValidatorInterface;
11
use Prettus\Repository\Events\RepositoryEntityCreated;
12
use Prettus\Repository\Events\RepositoryEntityUpdated;
13
use Prettus\Repository\Events\RepositoryEntityDeleted;
14
use Illuminate\Database\Query\Expression;
15
use Illuminate\Database\Query\Grammars;
16
use BadMethodCallException;
17
use RuntimeException;
18
19
/**
20
 * Class AbstractRepository
21
 *
22
 * @abstract
23
 */
24
abstract class AbstractRepository extends BaseRepository implements RepositoryInterface
25
{
26
    /**
27
     * @var string
28
     */
29
    protected $orderBy;
30
31
    /**
32
     * @var bool
33
     */
34
    protected $skipPresenter = true;
35
36
    /**
37
     * Reset Model
38
     *
39
     * @return AbstractRepository
40
     * @throws RepositoryException
41
     */
42 11
    public function resetModel()
43
    {
44 11
        parent::resetModel();
45 11
        return $this;
46
    }
47
48
    /**
49
     * Get Query
50
     *
51
     * @return Builder
52
     */
53 2
    public function getQuery()
54
    {
55 2
        $this->applyCriteria();
56 2
        $this->applyScope();
57
58 2
        return ($this->model instanceof Builder) ? $this->model : $this->model->newQuery();
59
    }
60
61
    /**
62
     * Search All
63
     *
64
     * @param array  $input         Array Imput
65
     * @param string $orderBy       String Order By
66
     * @param int    $limit         Integer Limit
67
     * @param bool   $skipPresenter Boolean Skip Presenter
68
     *
69
     * @return BuilderResultset
70
     */
71 1
    public function searchAll(array $input, $orderBy = '', $limit = null, $skipPresenter = true)
72
    {
73 1
        $orderBy = $orderBy?:$this->orderBy;
74
75 1
        $query = $this
76 1
            ->whereInputCriteria($input)
77 1
            ->orderBy($orderBy)
78 1
            ->skipPresenter($skipPresenter)
79 1
            ->getQuery()
80 1
            ->limit($limit);
81
82 1
        $this->resetModel();
83 1
        return app(BuilderResultset::class, [$query]);
84
    }
85
86
    /**
87
     * Search Paginator
88
     *
89
     * @param array    $input         Array Input
90
     * @param string   $orderBy       String Order By
91
     * @param int|null $limit         Integer Limit
92
     * @param bool     $skipPresenter Boolean Skip Presenter
93
     *
94
     * @return Paginator
95
     */
96 1
    public function search(array $input, $orderBy = '', $limit = null, $skipPresenter = true)
97
    {
98 1
        $orderBy = $orderBy?:$this->orderBy;
99
100 1
        return $this
101 1
            ->whereInputCriteria($input)
102 1
            ->orderBy($orderBy)
103 1
            ->skipPresenter($skipPresenter)
104 1
            ->paginate($limit);
0 ignored issues
show
Bug introduced by
It seems like $limit defined by parameter $limit on line 96 can also be of type integer; however, Prettus\Repository\Contr...ryInterface::paginate() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
105
    }
106
107
    /**
108
     * Get an array with the values of a given column.
109
     *
110
     * @param  string $column String Column
111
     * @param  string $key    String Key
112
     *
113
     * @return \Illuminate\Support\Collection
114
     */
115 1
    public function pluck($column, $key = null)
116
    {
117 1
        $this->applyCriteria();
118 1
        $this->applyScope();
119
120 1
        $lists = $this->model->pluck($column, $key);
121
122 1
        $this->resetModel();
123 1
        return $lists;
124
    }
125
126
    /**
127
     * Add an "order by" clause to the query.
128
     *
129
     * @param  string $columns   String Columns
130
     * @param  string $direction String Direction
131
     *
132
     * @return RepositoryInterface
133
     */
134 3
    public function orderBy($columns, $direction = 'asc')
135
    {
136 3
        if (!empty($columns)) {
137 3
            $columns = explode(',', $columns);
138 3
            foreach ($columns as $key => $column) {
139 3
                $column = explode(' ', $column);
140 3
                $column = array_filter($column);
141 3
                $column = array_pad($column, 2, '');
142 3
                list($field, $sort) = array_values($column);
143 3
                if (!empty($sort)) {
144 2
                    $direction = $sort;
145 2
                }
146 3
                $direction = strtoupper($direction);
147 3
                $direction = in_array($direction, ['ASC', 'DESC']) ? $direction : 'ASC';
148 3
                $this->model = $this->model->orderBy($field, $direction);
149 3
            }
150 3
        }
151
152 3
        return $this;
153
    }
154
155
    /**
156
     * Random
157
     *
158
     * @return RepositoryInterface
159
     */
160 4
    public function random()
161
    {
162 4
        $grammar = $this->model->getConnection()->getQueryGrammar();
163
164 4
        switch (true) {
165 4
            case $grammar instanceof Grammars\MySqlGrammar:
166 4
            case $grammar instanceof Grammars\SqlServerGrammar:
167 2
                $random = 'RAND()';
168 2
                break;
169 2
            case $grammar instanceof Grammars\PostgresGrammar:
170 2
            case $grammar instanceof Grammars\SQLiteGrammar:
171 2
                $random = 'RANDOM()';
172 2
        }
173
174 4
        $this->model = $this->model->orderBy(new Expression($random));
0 ignored issues
show
Bug introduced by
The variable $random does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
175
176 4
        return $this;
177
    }
178
    
179
    /**
180
     * Count
181
     *
182
     * @param array $input Array Input
183
     *
184
     * @return int
185
     */
186 1
    public function count(array $input = array())
187
    {
188 1
        $this->applyCriteria();
189 1
        $this->applyScope();
190
        
191 1
        $this->whereInputCriteria($input);
192
        
193 1
        $count = $this->model->count();
194
        
195 1
        $this->resetModel();
196 1
        return $count;
197
    }
198
    
199
    /**
200
     * Max
201
     *
202
     * @param mixed $field Mixed Field
203
     * @param array $input Array Input
204
     *
205
     * @return mixed
206
     */
207 1
    public function max($field, array $input = array())
208
    {
209 1
        $this->applyCriteria();
210 1
        $this->applyScope();
211
    
212 1
        $this->whereInputCriteria($input);
213
    
214 1
        $max = $this->model->max($field);
215
    
216 1
        $this->resetModel();
217 1
        return $max;
218
    }
219
220
    /**
221
     * Min
222
     *
223
     * @param mixed $field Mixed Field
224
     * @param array $input Array Input
225
     *
226
     * @return mixed
227
     */
228 1
    public function min($field, array $input = array())
229
    {
230 1
        $this->applyCriteria();
231 1
        $this->applyScope();
232
    
233 1
        $this->whereInputCriteria($input);
234
    
235 1
        $max = $this->model->min($field);
236
    
237 1
        $this->resetModel();
238 1
        return $max;
239
    }
240
241
    /**
242
     * Sum
243
     *
244
     * @param mixed $field Mixed Field
245
     * @param array $input Array Input
246
     *
247
     * @return float
248
     */
249 1
    public function sum($field, array $input = array())
250
    {
251 1
        $this->applyCriteria();
252 1
        $this->applyScope();
253
    
254 1
        $this->whereInputCriteria($input);
255
    
256 1
        $max = $this->model->sum($field);
257
    
258 1
        $this->resetModel();
259 1
        return $max;
260
    }
261
262
    /**
263
     * Average
264
     *
265
     * @param mixed $field Mixed Field
266
     * @param array $input Array Input
267
     *
268
     * @return int
269
     */
270 1
    public function avg($field, array $input = array())
271
    {
272 1
        $this->applyCriteria();
273 1
        $this->applyScope();
274
    
275 1
        $this->whereInputCriteria($input);
276
    
277 1
        $avg = $this->model->avg($field);
278
    
279 1
        $this->resetModel();
280 1
        return $avg;
281
    }
282
283
    /**
284
     * Reorder
285
     *
286
     * @param string $field Field Order
287
     *
288
     * @return boolean
289
     */
290 4
    public function reorder($field, $input = null)
291
    {
292 4
        $self = $this;
293 4
        $conn = $this->model->getConnection();
294
295 4
        $reorder = function ($statement, $value) use ($self, $conn, $input, $field) {
296 3
            $conn->statement($statement);
297 3
            $data = [$field => $conn->raw($value)];
298
299 3
            return $self->whereInputCriteria($input)
300 3
                        ->orderBy($field)
301 3
                        ->getQuery()
302 3
                        ->update($data);
303 4
        };
304
305 4
        $return = false;
0 ignored issues
show
Unused Code introduced by
$return is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
306
307 4
        switch (true) {
308 4
            case $conn instanceof \Illuminate\Database\MySqlConnection:
309 1
                $statement = "SET @rownum := 0";
310 1
                $value = "(@rownum := @rownum+1)";
311 1
                $return = $reorder($statement, $value);
312 1
                break;
313
314 3
            case $conn instanceof \Illuminate\Database\PostgresConnection:
315 1
                $statement = "CREATE TEMPORARY SEQUENCE rownum_seq";
316 1
                $value = "NETVAL('rownum_seq')";
317 1
                $return = $reorder($statement, $value);
318 1
                break;
319
320 2
            case $conn instanceof \Illuminate\Database\SqlServerConnection:
321 1
                $statement = "DECLARE @rownum int; SET @rownum = 0";
322 1
                $value = "(@rownum = @rownum+1)";
323 1
                $return = $reorder($statement, $value);
324 1
                break;
325 1
            default:
326 1
                $return = false;
327 1
        }
328
329 4
        if ($return) {
330 3
            return $return;
331
        }
332
333 1
        throw new RuntimeException(sprintf("Reorder not valid for connection (%s)", get_class($conn)));
334
    }
335
336
    /**
337
     * Where InputCriteria
338
     *
339
     * @param array $input Array Input
340
     *
341
     * @return RepositoryInterface
342
     */
343 8
    public function whereInputCriteria(array $input = array())
344
    {
345 8
        if (count($input)) {
346 8
            $criteria = app(InputCriteria::class, [$input]);
347 8
            $this->model = $criteria->apply($this->model, $this);
348 8
        }
349
350 8
        return $this;
351
    }
352
    
353
    /**
354
     * Validar
355
     *
356
     * @param array  $attributes
357
     * @param string $action
358
     * @param string $id
359
     *
360
     * @return bool
361
     */
362 2
    public function validar(array $attributes, $action, $id = null)
363
    {
364 2
        $return = false;
365
366 2
        if (!is_null($this->validator)) {
367
            // we should pass data that has been casts by the model
368
            // to make sure data type are same because validator may need to use
369
            // this data to compare with data that fetch from database.
370 2
            $model = $this->model->newInstance()->forceFill($attributes);
371 2
            $attributes = array_merge($attributes, $model->toArray());
372
373 2
            $validator = $this->validator->with($attributes);
374
375 2
            if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
376 1
                $validator->setId($id);
377 1
            }
378
379 2
            $return = $validator->passesOrFail($action);
380 2
        }
381
382 2
        return $return;
383
    }
384
385
    /**
386
     * Save a new model in repository
387
     *
388
     * @throws ValidatorException
389
     * @param array $attributes Array Attributes
390
     * @return mixed
391
     */
392 1
    public function create(array $attributes)
393
    {
394 1
        $this->validar($attributes, ValidatorInterface::RULE_CREATE);
395
396 1
        $model = $this->model->newInstance($attributes);
397 1
        $model->save();
398 1
        $this->resetModel();
399
400 1
        event(new RepositoryEntityCreated($this, $model));
401
402 1
        return $this->parserResult($model);
403
    }
404
405
    /**
406
     * Update a model in repository by id
407
     *
408
     * @throws ValidatorException
409
     * @param array $attributes Array Attributes
410
     * @param int   $id         Integer Id
411
     * @return mixed
412
     */
413 1
    public function update(array $attributes, $id)
414
    {
415 1
        $this->applyScope();
416
417 1
        $this->validar($attributes, ValidatorInterface::RULE_UPDATE, $id);
418
419 1
        $temporarySkipPresenter = $this->skipPresenter;
420
421 1
        $this->skipPresenter(true);
422
423 1
        $model = $this->model->findOrFail($id);
424 1
        $model->fill($attributes);
425 1
        $model->save();
426
427 1
        $this->skipPresenter($temporarySkipPresenter);
428 1
        $this->resetModel();
429
430 1
        event(new RepositoryEntityUpdated($this, $model));
431
432 1
        return $this->parserResult($model);
433
    }
434
435
    /**
436
     * Delete multiple entities by given criteria.
437
     *
438
     * @param array $where
439
     *
440
     * @return int
441
     */
442 1
    public function deleteWhere(array $where)
443
    {
444 1
        $this->applyCriteria();
445 1
        $this->applyScope();
446
        
447 1
        $temporarySkipPresenter = $this->skipPresenter;
448 1
        $this->skipPresenter(true);
449
450 1
        $this->whereInputCriteria($where);
451
452 1
        $deleted = $this->model->delete();
453
454 1
        event(new RepositoryEntityDeleted($this, $this->model));
455
456 1
        $this->skipPresenter($temporarySkipPresenter);
457 1
        $this->resetModel();
458
459 1
        return $deleted;
460
    }
461
462
    /**
463
     * Update multiple entities by given criteria.
464
     *
465
     * @param array $where
466
     *
467
     * @return int
468
     */
469 1
    public function updateWhere(array $attributes, array $where)
470
    {
471 1
        $this->applyCriteria();
472 1
        $this->applyScope();
473
474 1
        $temporarySkipPresenter = $this->skipPresenter;
475 1
        $this->skipPresenter(true);
476
477 1
        $this->whereInputCriteria($where);
478
479 1
        $updated = $this->model->update($attributes);
480
481 1
        event(new RepositoryEntityUpdated($this, $this->model));
482
483 1
        $this->skipPresenter($temporarySkipPresenter);
484 1
        $this->resetModel();
485
486 1
        return $updated;
487
    }
488
489
    /**
490
     * Handle dynamic method calls into the method.
491
     *
492
     * @param  string  $method
493
     * @param  array   $parameters
494
     * @return mixed
495
     *
496
     * @throws BadMethodCallException
497
     */
498 7
    public function __call($method, $parameters)
499
    {
500 7
        $pattern = '/^(((where|orWhere).*)|limit|groupBy|join|leftJoin|rightJoin|crossJoin)$/';
501 7
        if (preg_match($pattern, $method)) {
502 6
            $this->model = call_user_func_array([$this->model, $method], $parameters);
503 6
            return $this;
504
        }
505
506 1
        $className = static::class;
507 1
        throw new BadMethodCallException("Call to undefined method {$className}::{$method}()");
508
    }
509
}
510