Completed
Push — master ( 2b998c...4f2888 )
by Renato
06:41
created

AbstractRepository   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 415
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 98.81%

Importance

Changes 0
Metric Value
dl 0
loc 415
ccs 166
cts 168
cp 0.9881
rs 8.8
c 0
b 0
f 0
wmc 36
lcom 1
cbo 9

17 Methods

Rating   Name   Duplication   Size   Complexity  
A resetModel() 0 5 1
A getQuery() 0 7 2
A searchAll() 0 14 2
A search() 0 10 2
A pluck() 0 10 1
B orderBy() 0 20 5
B random() 0 19 5
A count() 0 12 1
A max() 0 12 1
A min() 0 12 1
A sum() 0 12 1
A avg() 0 12 1
B reorder() 0 43 5
A whereInputCriteria() 0 9 2
A create() 0 20 2
B update() 0 29 2
A __call() 0 11 2
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
     * Save a new model in repository
353
     *
354
     * @throws ValidatorException
355
     * @param array $attributes Array Attributes
356
     * @return mixed
357
     */
358 1
    public function create(array $attributes)
359
    {
360 1
        if (!is_null($this->validator)) {
361
            // we should pass data that has been casts by the model
362
            // to make sure data type are same because validator may need to use
363
            // this data to compare with data that fetch from database.
364 1
            $model = $this->model->newInstance()->forceFill($attributes);
365 1
            $attributes = array_merge($attributes, $model->toArray());
366
367 1
            $this->validator->with($attributes)->passesOrFail(ValidatorInterface::RULE_CREATE);
368 1
        }
369
370 1
        $model = $this->model->newInstance($attributes);
371 1
        $model->save();
372 1
        $this->resetModel();
373
374 1
        event(new RepositoryEntityCreated($this, $model));
375
376 1
        return $this->parserResult($model);
377
    }
378
379
    /**
380
     * Update a model in repository by id
381
     *
382
     * @throws ValidatorException
383
     * @param array $attributes Array Attributes
384
     * @param int   $id         Integer Id
385
     * @return mixed
386
     */
387 1
    public function update(array $attributes, $id)
388
    {
389 1
        $this->applyScope();
390
391 1
        if (!is_null($this->validator)) {
392
            // we should pass data that has been casts by the model
393
            // to make sure data type are same because validator may need to use
394
            // this data to compare with data that fetch from database.
395 1
            $model = $this->model->newInstance()->forceFill($attributes);
396 1
            $attributes = array_merge($attributes, $model->toArray());
397
398 1
            $this->validator->with($attributes)->setId($id)->passesOrFail(ValidatorInterface::RULE_UPDATE);
399 1
        }
400
401 1
        $temporarySkipPresenter = $this->skipPresenter;
402
403 1
        $this->skipPresenter(true);
404
405 1
        $model = $this->model->findOrFail($id);
406 1
        $model->fill($attributes);
407 1
        $model->save();
408
409 1
        $this->skipPresenter($temporarySkipPresenter);
410 1
        $this->resetModel();
411
412 1
        event(new RepositoryEntityUpdated($this, $model));
413
414 1
        return $this->parserResult($model);
415
    }
416
417
    /**
418
     * Handle dynamic method calls into the method.
419
     *
420
     * @param  string  $method
421
     * @param  array   $parameters
422
     * @return mixed
423
     *
424
     * @throws BadMethodCallException
425
     */
426 7
    public function __call($method, $parameters)
427
    {
428 7
        $pattern = '/^(((where|orWhere).*)|limit|groupBy|join|leftJoin|rightJoin|crossJoin)$/';
429 7
        if (preg_match($pattern, $method)) {
430 6
            $this->model = call_user_func_array([$this->model, $method], $parameters);
431 6
            return $this;
432
        }
433
434 1
        $className = static::class;
435 1
        throw new BadMethodCallException("Call to undefined method {$className}::{$method}()");
436
    }
437
}
438