Completed
Push — master ( 0d8720...e4483e )
by Renato
06:38
created

AbstractRepository::validar()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 3
dl 0
loc 20
ccs 10
cts 10
cp 1
crap 3
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 Illuminate\Database\Query\Expression;
14
use Illuminate\Database\Query\Grammars;
15
use BadMethodCallException;
16
use RuntimeException;
17
18
/**
19
 * Class AbstractRepository
20
 *
21
 * @abstract
22
 */
23
abstract class AbstractRepository extends BaseRepository implements RepositoryInterface
24
{
25
    /**
26
     * @var string
27
     */
28
    protected $orderBy;
29
30
    /**
31
     * @var bool
32
     */
33
    protected $skipPresenter = true;
34
35
    /**
36
     * Reset Model
37
     *
38
     * @return AbstractRepository
39
     * @throws RepositoryException
40
     */
41 9
    public function resetModel()
42
    {
43 9
        parent::resetModel();
44 9
        return $this;
45
    }
46
47
    /**
48
     * Get Query
49
     *
50
     * @return Builder
51
     */
52 2
    public function getQuery()
53
    {
54 2
        $this->applyCriteria();
55 2
        $this->applyScope();
56
57 2
        return ($this->model instanceof Builder) ? $this->model : $this->model->newQuery();
58
    }
59
60
    /**
61
     * Search All
62
     *
63
     * @param array  $input         Array Imput
64
     * @param string $orderBy       String Order By
65
     * @param int    $limit         Integer Limit
66
     * @param bool   $skipPresenter Boolean Skip Presenter
67
     *
68
     * @return BuilderResultset
69
     */
70 1
    public function searchAll(array $input, $orderBy = '', $limit = null, $skipPresenter = true)
71
    {
72 1
        $orderBy = $orderBy?:$this->orderBy;
73
74 1
        $query = $this
75 1
            ->whereInputCriteria($input)
76 1
            ->orderBy($orderBy)
77 1
            ->skipPresenter($skipPresenter)
78 1
            ->getQuery()
79 1
            ->limit($limit);
80
81 1
        $this->resetModel();
82 1
        return app(BuilderResultset::class, [$query]);
83
    }
84
85
    /**
86
     * Search Paginator
87
     *
88
     * @param array    $input         Array Input
89
     * @param string   $orderBy       String Order By
90
     * @param int|null $limit         Integer Limit
91
     * @param bool     $skipPresenter Boolean Skip Presenter
92
     *
93
     * @return Paginator
94
     */
95 1
    public function search(array $input, $orderBy = '', $limit = null, $skipPresenter = true)
96
    {
97 1
        $orderBy = $orderBy?:$this->orderBy;
98
99 1
        return $this
100 1
            ->whereInputCriteria($input)
101 1
            ->orderBy($orderBy)
102 1
            ->skipPresenter($skipPresenter)
103 1
            ->paginate($limit);
0 ignored issues
show
Bug introduced by
It seems like $limit defined by parameter $limit on line 95 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...
104
    }
105
106
    /**
107
     * Get an array with the values of a given column.
108
     *
109
     * @param  string $column String Column
110
     * @param  string $key    String Key
111
     *
112
     * @return \Illuminate\Support\Collection
113
     */
114 1
    public function pluck($column, $key = null)
115
    {
116 1
        $this->applyCriteria();
117 1
        $this->applyScope();
118
119 1
        $lists = $this->model->pluck($column, $key);
120
121 1
        $this->resetModel();
122 1
        return $lists;
123
    }
124
125
    /**
126
     * Add an "order by" clause to the query.
127
     *
128
     * @param  string $columns   String Columns
129
     * @param  string $direction String Direction
130
     *
131
     * @return RepositoryInterface
132
     */
133 3
    public function orderBy($columns, $direction = 'asc')
134
    {
135 3
        if (!empty($columns)) {
136 3
            $columns = explode(',', $columns);
137 3
            foreach ($columns as $key => $column) {
138 3
                $column = explode(' ', $column);
139 3
                $column = array_filter($column);
140 3
                $column = array_pad($column, 2, '');
141 3
                list($field, $sort) = array_values($column);
142 3
                if (!empty($sort)) {
143 2
                    $direction = $sort;
144 2
                }
145 3
                $direction = strtoupper($direction);
146 3
                $direction = in_array($direction, ['ASC', 'DESC']) ? $direction : 'ASC';
147 3
                $this->model = $this->model->orderBy($field, $direction);
148 3
            }
149 3
        }
150
151 3
        return $this;
152
    }
153
154
    /**
155
     * Random
156
     *
157
     * @return RepositoryInterface
158
     */
159 4
    public function random()
160
    {
161 4
        $grammar = $this->model->getConnection()->getQueryGrammar();
162
163 4
        switch (true) {
164 4
            case $grammar instanceof Grammars\MySqlGrammar:
165 4
            case $grammar instanceof Grammars\SqlServerGrammar:
166 2
                $random = 'RAND()';
167 2
                break;
168 2
            case $grammar instanceof Grammars\PostgresGrammar:
169 2
            case $grammar instanceof Grammars\SQLiteGrammar:
170 2
                $random = 'RANDOM()';
171 2
                break;
172
        }
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;
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
        }
326
327 4
        if ($return) {
328 3
            return $return;
329
        }
330
331 1
        throw new RuntimeException(sprintf("Reorder not valid for connection (%s)", get_class($conn)));
332
    }
333
334
    /**
335
     * Where InputCriteria
336
     *
337
     * @param array $input Array Input
338
     *
339
     * @return RepositoryInterface
340
     */
341 8
    public function whereInputCriteria(array $input = array())
342
    {
343 8
        if (count($input)) {
344 8
            $criteria = app(InputCriteria::class, [$input]);
345 8
            $this->model = $criteria->apply($this->model, $this);
346 8
        }
347
348 8
        return $this;
349
    }
350
    
351
    /**
352
     * Validar
353
     *
354
     * @param array  $attributes
355
     * @param string $action
356
     * @param string $id
357
     *
358 1
     * @return bool
359
     */
360 1
    public function validar(array $attributes, $action, $id = null)
361
    {
362
        if (!is_null($this->validator)) {
363
            // we should pass data that has been casts by the model
364 1
            // to make sure data type are same because validator may need to use
365 1
            // this data to compare with data that fetch from database.
366
            $model = $this->model->newInstance()->forceFill($attributes);
367 1
            $attributes = array_merge($attributes, $model->toArray());
368 1
369
            $validator = $this->validator->with($attributes);
370 1
371 1
            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...
372 1
                $validator->setId($id);
373
            }
374 1
375
            return $validator->passesOrFail($action);
376 1
        }
377
378
        return false;
379
    }
380
381
    /**
382
     * Save a new model in repository
383
     *
384
     * @throws ValidatorException
385
     * @param array $attributes Array Attributes
386
     * @return mixed
387 1
     */
388
    public function create(array $attributes)
389 1
    {
390
        $this->validar($attributes, ValidatorInterface::RULE_CREATE);
391 1
392
        $model = $this->model->newInstance($attributes);
393
        $model->save();
394
        $this->resetModel();
395 1
396 1
        event(new RepositoryEntityCreated($this, $model));
397
398 1
        return $this->parserResult($model);
399 1
    }
400
401 1
    /**
402
     * Update a model in repository by id
403 1
     *
404
     * @throws ValidatorException
405 1
     * @param array $attributes Array Attributes
406 1
     * @param int   $id         Integer Id
407 1
     * @return mixed
408
     */
409 1
    public function update(array $attributes, $id)
410 1
    {
411
        $this->applyScope();
412 1
413
        $this->validar($attributes, ValidatorInterface::RULE_UPDATE, $id);
414 1
415
        $temporarySkipPresenter = $this->skipPresenter;
416
417
        $this->skipPresenter(true);
418
419
        $model = $this->model->findOrFail($id);
420
        $model->fill($attributes);
421
        $model->save();
422
423
        $this->skipPresenter($temporarySkipPresenter);
424
        $this->resetModel();
425
426 7
        event(new RepositoryEntityUpdated($this, $model));
427
428 7
        return $this->parserResult($model);
429 7
    }
430 6
431 6
    /**
432
     * Handle dynamic method calls into the method.
433
     *
434 1
     * @param  string  $method
435 1
     * @param  array   $parameters
436
     * @return mixed
437
     *
438
     * @throws BadMethodCallException
439
     */
440
    public function __call($method, $parameters)
441
    {
442
        $pattern = '/^(((where|orWhere).*)|limit|groupBy|join|leftJoin|rightJoin|crossJoin)$/';
443
        if (preg_match($pattern, $method)) {
444
            $this->model = call_user_func_array([$this->model, $method], $parameters);
445
            return $this;
446
        }
447
448
        $className = static::class;
449
        throw new BadMethodCallException("Call to undefined method {$className}::{$method}()");
450
    }
451
}
452