Completed
Push — master ( 03f4af...5ee1ce )
by Renato
03:39
created

AbstractRepository::joinSub()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1.008

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 7
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
ccs 4
cts 5
cp 0.8
crap 1.008
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
     * @return Model
38
     * @throws RepositoryException
39
     */
40 29
    public function makeModel()
41
    {
42 29
        parent::makeModel();
43 29
        return $this->model = $this->model->newQuery();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->model->newQuery() of type object<Illuminate\Database\Eloquent\Builder> is incompatible with the declared type object<Illuminate\Database\Eloquent\Model> of property $model.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Bug Best Practice introduced by
The return type of return $this->model = $this->model->newQuery(); (Illuminate\Database\Eloquent\Builder) is incompatible with the return type of the parent method Prettus\Repository\Eloqu...seRepository::makeModel of type Illuminate\Database\Eloquent\Model.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
44
    }
45
46
    /**
47
     * Reset Model
48
     *
49
     * @return AbstractRepository
50
     * @throws RepositoryException
51
     */
52 10
    public function resetModel()
53
    {
54 10
        parent::resetModel();
55 10
        return $this;
56
    }
57
58
    /**
59
     * Get Query
60
     *
61
     * @return Builder
62
     */
63 8
    public function getQuery()
64
    {
65 8
        $this->applyCriteria();
66 8
        $this->applyScope();
67
68 8
        $model = $this->model;
69
70 8
        $this->resetModel();
71 8
        return $model;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $model; (Illuminate\Database\Eloquent\Model) is incompatible with the return type declared by the interface NwLaravel\Repositories\R...toryInterface::getQuery of type Illuminate\Database\Eloquent\Builder.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
72
    }
73
74
    /**
75
     * Search All
76
     *
77
     * @param array  $input         Array Imput
78
     * @param string $orderBy       String Order By
79
     * @param int    $limit         Integer Limit
80
     * @param bool   $skipPresenter Boolean Skip Presenter
81
     *
82
     * @return BuilderResultset
83
     */
84 1
    public function searchAll(array $input, $orderBy = '', $limit = null, $skipPresenter = true)
85
    {
86 1
        $orderBy = $orderBy?:$this->orderBy;
87
88 1
        $query = $this
89 1
            ->whereInputCriteria($input)
90 1
            ->orderBy($orderBy)
91 1
            ->skipPresenter($skipPresenter)
92 1
            ->getQuery()
93 1
            ->limit($limit);
94
95 1
        return new BuilderResultset($query);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \NwLaravel\Re...ilderResultset($query); (NwLaravel\Resultset\BuilderResultset) is incompatible with the return type declared by the interface NwLaravel\Repositories\R...oryInterface::searchAll of type NwLaravel\Repositories\N...ultset\BuilderResultset.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
96
    }
97
98
    /**
99
     * Search Paginator
100
     *
101
     * @param array    $input         Array Input
102
     * @param string   $orderBy       String Order By
103
     * @param int|null $limit         Integer Limit
104
     * @param bool     $skipPresenter Boolean Skip Presenter
105
     *
106
     * @return Paginator
107
     */
108 1
    public function search(array $input, $orderBy = '', $limit = null, $skipPresenter = true)
109
    {
110 1
        $orderBy = $orderBy?:$this->orderBy;
111
112 1
        return $this
113 1
            ->whereInputCriteria($input)
114 1
            ->orderBy($orderBy)
115 1
            ->skipPresenter($skipPresenter)
116 1
            ->paginate($limit);
0 ignored issues
show
Bug introduced by
It seems like $limit defined by parameter $limit on line 108 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...
117
    }
118
119
    /**
120
     * ResultSet
121
     *
122
     * @param int $limit Integer Limit
123
     *
124
     * @return BuilderResultset
125
     */
126
    public function resultset($limit = null)
127
    {
128
        $query = $this->getQuery()->limit($limit);
129
130
        return new BuilderResultset($query);
131
    }
132
133
    /**
134
     * Get an array with the values of a given column.
135
     *
136
     * @param  string $column String Column
137
     * @param  string $key    String Key
138
     *
139
     * @return \Illuminate\Support\Collection
140
     */
141 1
    public function pluck($column, $key = null)
142
    {
143 1
        return $this->getQuery()->pluck($column, $key);
144
    }
145
146
    /**
147
     * Add an "order by" clause to the query.
148
     *
149
     * @param  string $columns   String Columns
150
     * @param  string $direction String Direction
151
     *
152
     * @return RepositoryInterface
153
     */
154 3
    public function orderBy($columns, $direction = 'asc')
155
    {
156 3
        if (!empty($columns)) {
157 3
            $columns = explode(',', $columns);
158 3
            foreach ($columns as $key => $column) {
159 3
                $column = explode(' ', $column);
160 3
                $column = array_filter($column);
161 3
                $column = array_pad($column, 2, '');
162 3
                list($field, $sort) = array_values($column);
163 3
                if (!empty($sort)) {
164 2
                    $direction = $sort;
165 2
                }
166 3
                $direction = strtoupper($direction);
167 3
                $direction = in_array($direction, ['ASC', 'DESC']) ? $direction : 'ASC';
168 3
                $this->model = $this->model->orderBy($field, $direction);
169 3
            }
170 3
        }
171
172 3
        return $this;
173
    }
174
175
    /**
176
     * Random
177
     *
178
     * @return RepositoryInterface
179
     */
180 4
    public function random()
181
    {
182 4
        $grammar = $this->model->getConnection()->getQueryGrammar();
183
184 4
        switch (true) {
185 4
            case $grammar instanceof Grammars\MySqlGrammar:
186 4
            case $grammar instanceof Grammars\SqlServerGrammar:
187 2
                $random = 'RAND()';
188 2
                break;
189 2
            case $grammar instanceof Grammars\PostgresGrammar:
190 2
            case $grammar instanceof Grammars\SQLiteGrammar:
191 2
                $random = 'RANDOM()';
192 2
        }
193
194 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...
195
196 4
        return $this;
197
    }
198
    
199
    /**
200
     * Count
201
     *
202
     * @param array $input Array Input
203
     *
204
     * @return int
205
     */
206 1
    public function count(array $input = array())
207
    {
208 1
        $this->whereInputCriteria($input);
209
210 1
        return $this->getQuery()->count();
211
    }
212
    
213
    /**
214
     * Max
215
     *
216
     * @param mixed $field Mixed Field
217
     * @param array $input Array Input
218
     *
219
     * @return mixed
220
     */
221 1
    public function max($field, array $input = array())
222
    {
223 1
        $this->whereInputCriteria($input);
224
225 1
        return $this->getQuery()->max($field);
226
    }
227
228
    /**
229
     * Min
230
     *
231
     * @param mixed $field Mixed Field
232
     * @param array $input Array Input
233
     *
234
     * @return mixed
235
     */
236 1
    public function min($field, array $input = array())
237
    {
238 1
        $this->whereInputCriteria($input);
239
240 1
        return $this->getQuery()->min($field);
241
    }
242
243
    /**
244
     * Sum
245
     *
246
     * @param mixed $field Mixed Field
247
     * @param array $input Array Input
248
     *
249
     * @return float
250
     */
251 1
    public function sum($field, array $input = array())
252
    {
253 1
        $this->whereInputCriteria($input);
254
255 1
        return $this->getQuery()->sum($field);
256
    }
257
258
    /**
259
     * Average
260
     *
261
     * @param mixed $field Mixed Field
262
     * @param array $input Array Input
263
     *
264
     * @return int
265
     */
266 1
    public function avg($field, array $input = array())
267
    {
268 1
        $this->whereInputCriteria($input);
269
270 1
        return $this->getQuery()->avg($field);
271
    }
272
273
    /**
274
     * Order Up
275
     *
276
     * @param Model  $model
277
     * @param string $field Field Order
278
     * @param array  $input Array Where
279
     *
280
     * @return boolean
281
     */
282
    public function orderUp($model, $field, array $input = [])
283
    {
284
        $input["{$field} <= ?"] = $model->{$field};
285
        $input["id != ?"] = $model->id;
286
        return $this->reorder($model, $field, $input, 'DESC');
287
    }
288
289
    /**
290
     * Order Top
291
     *
292
     * @param Model  $model
293
     * @param string $field Field Order
294
     * @param array  $input Array Where
295
     *
296
     * @return boolean
297
     */
298
    public function orderTop($model, $field, array $input = [])
299
    {
300
        return $this->reorder($model, $field, $input, 'ASC');
301
    }
302
303
    /**
304
     * Order Down
305
     *
306
     * @param Model  $model
307
     * @param string $field Field Order
308
     * @param array  $input Array Where
309
     *
310
     * @return boolean
311
     */
312
    public function orderDown($model, $field, array $input = [])
313
    {
314
        $input["{$field} >= ?"] = $model->{$field};
315
        $input["id != ?"] = $model->id;
316
        return $this->reorder($model, $field, $input, 'ASC');
317
    }
318
319
    /**
320
     * Order Bottom
321
     *
322
     * @param Model  $model
323
     * @param string $field Field Order
324
     * @param array  $input Array Where
325
     *
326
     * @return boolean
327
     */
328
    public function orderBottom($model, $field, array $input = [])
329
    {
330
        return $this->reorder($model, $field, $input, 'DESC');
331
    }
332
333
    /**
334
     * Reorder
335
     *
336
     * @param Model  $model
337
     * @param string $field Field Order
338
     * @param array  $input Array Where
339
     * @param string $sort  Sort
340
     *
341
     * @return boolean
342
     */
343
    protected function reorder($model, $field, array $input, $sort)
344
    {
345
        if (!$model->exists) {
346
            return false;
347
        }
348
349
        $order = $model->{$field};
350
351
        $anterior = $this->whereInputCriteria($input)->orderBy($field, $sort)->first();
352
353
        if ($anterior) {
354
            $model->{$field} = $anterior->{$field};
355
            $model->save();
356
357
            $anterior->{$field} = $order;
358
            $anterior->save();
359
        }
360
361
        event(new RepositoryEntityUpdated($this, $model));
362
363
        return true;
364
    }
365
366
    /**
367
     * Where InputCriteria
368
     *
369
     * @param array $input Array Input
370
     *
371
     * @return RepositoryInterface
372
     */
373 7
    public function whereInputCriteria(array $input = array())
374
    {
375 7
        if (count($input)) {
376 6
            $criteria = new InputCriteria($input);
377 6
            $this->model = $criteria->apply($this->model, $this);
0 ignored issues
show
Documentation introduced by
$this->model is of type object<Illuminate\Database\Eloquent\Model>, but the function expects a object<NwLaravel\Repositories\Criterias\Builder>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
378 6
        }
379
380 7
        return $this;
381
    }
382
    
383
    /**
384
     * Validar
385
     *
386
     * @param array  $attributes
387
     * @param string $action
388
     * @param string $id
389
     *
390
     * @return bool
391
     */
392 2
    public function validar(array $attributes, $action, $id = null)
393
    {
394 2
        $return = false;
395
396 2
        if (!is_null($this->validator)) {
397
            // we should pass data that has been casts by the model
398
            // to make sure data type are same because validator may need to use
399
            // this data to compare with data that fetch from database.
400 2
            $model = $this->model->newModelInstance()->fill($attributes);
401
            $attributes = array_merge($attributes, $model->toArray());
402
403
            $validator = $this->validator->with($attributes);
404
405
            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...
406
                $validator->setId($id);
407
            }
408
409
            $return = $validator->passesOrFail($action);
410
        }
411
412
        return $return;
413
    }
414
415
    /**
416
     * Save a new model in repository
417
     *
418
     * @throws ValidatorException
419
     * @param array $attributes Array Attributes
420
     * @return mixed
421
     */
422 1
    public function create(array $attributes)
423
    {
424 1
        $this->validar($attributes, ValidatorInterface::RULE_CREATE);
425
426
        $model = $this->model->newModelInstance($attributes);
427
        $model->save();
428
        $this->resetModel();
429
430
        event(new RepositoryEntityCreated($this, $model));
431
432
        return $this->parserResult($model);
433
    }
434
435
    /**
436
     * Update a model in repository by id
437
     *
438
     * @throws ValidatorException
439
     * @param array $attributes Array Attributes
440
     * @param int   $id         Integer Id
441
     * @return mixed
442
     */
443 1
    public function update(array $attributes, $id)
444
    {
445 1
        $this->applyScope();
446
447 1
        $this->validar($attributes, ValidatorInterface::RULE_UPDATE, $id);
448
449
        $temporarySkipPresenter = $this->skipPresenter;
450
451
        $this->skipPresenter(true);
452
453
        $model = $this->model->findOrFail($id);
454
        $model->fill($attributes);
455
        $model->save();
456
457
        $this->skipPresenter($temporarySkipPresenter);
458
        $this->resetModel();
459
460
        event(new RepositoryEntityUpdated($this, $model));
461
462
        return $this->parserResult($model);
463
    }
464
465
    /**
466
     * Delete multiple entities by given criteria.
467
     *
468
     * @param array $where
469
     *
470
     * @return boolean|null
471
     */
472 1
    public function deleteWhere(array $where)
473
    {
474 1
        $this->applyCriteria();
475 1
        $this->applyScope();
476
477 1
        $temporarySkipPresenter = $this->skipPresenter;
478 1
        $this->skipPresenter(true);
479
480 1
        $this->whereInputCriteria($where);
481
482 1
        $deleted = $this->model->delete();
483
484 1
        $model = $this->model instanceof Builder ? $this->model->getModel() : $this->model;
485 1
        event(new RepositoryEntityDeleted($this, $model));
486
487 1
        $this->skipPresenter($temporarySkipPresenter);
488 1
        $this->resetModel();
489
490 1
        return $deleted;
491
    }
492
493
    /**
494
     * Update multiple entities by given criteria.
495
     *
496
     * @param array $where
497
     *
498
     * @return boolean|null
499
     */
500 1
    public function updateWhere(array $attributes, array $where)
501
    {
502 1
        $this->applyCriteria();
503 1
        $this->applyScope();
504
505 1
        $temporarySkipPresenter = $this->skipPresenter;
506 1
        $this->skipPresenter(true);
507
508 1
        $this->whereInputCriteria($where);
509
510 1
        $updated = $this->model->update($attributes);
511
512 1
        $this->skipPresenter($temporarySkipPresenter);
513 1
        $this->resetModel();
514
515 1
        $model = $this->model instanceof Builder ? $this->model->getModel() : $this->model;
516 1
        event(new RepositoryEntityUpdated($this, $model));
517
518 1
        return $updated;
519
    }
520
521
    /**
522
     * Add a subquery join clause to the query.
523
     *
524
     * @param  \Closure|\Illuminate\Database\Query\Builder|string $query
525
     * @param  string  $as
526
     * @param  string  $first
527
     * @param  string|null  $operator
528
     * @param  string|null  $second
529
     * @param  string  $type
530
     * @param  bool    $where
531 7
     * @return \Illuminate\Database\Query\Builder|static
532
     *
533 7
     * @throws \InvalidArgumentException
534 7
     */
535 6
    public function joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false)
536 6
    {
537
        $sql = $query->toSql();
0 ignored issues
show
Bug introduced by
The method toSql does only exist in Illuminate\Database\Query\Builder, but not in Closure.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
538
        $bindings = $query->getBindings();
0 ignored issues
show
Bug introduced by
The method getBindings does only exist in Illuminate\Database\Query\Builder, but not in Closure.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
539 1
        $expression = '('.$sql.') as '.$this->model->getGrammar()->wrap($as);
540 1
        $this->model->addBinding($bindings, 'join');
541
542
        return $this->join(new Expression($expression), $first, $operator, $second, $type, $where);
0 ignored issues
show
Documentation Bug introduced by
The method join does not exist on object<NwLaravel\Reposit...ent\AbstractRepository>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
543
    }
544 1
545 1
    /**
546
     * Add a subquery left join to the query.
547
     *
548
     * @param  \Closure|\Illuminate\Database\Query\Builder|string $query
549
     * @param  string  $as
550
     * @param  string  $first
551
     * @param  string|null  $operator
552
     * @param  string|null  $second
553
     * @return \Illuminate\Database\Query\Builder|static
554
     */
555
    public function leftJoinSub($query, $as, $first, $operator = null, $second = null)
556
    {
557
        return $this->joinSub($query, $as, $first, $operator, $second, 'left');
558
    }
559
560
    /**
561
     * Add a subquery right join to the query.
562
     *
563
     * @param  \Closure|\Illuminate\Database\Query\Builder|string $query
564
     * @param  string  $as
565
     * @param  string  $first
566
     * @param  string|null  $operator
567
     * @param  string|null  $second
568
     * @return \Illuminate\Database\Query\Builder|static
569
     */
570
    public function rightJoinSub($query, $as, $first, $operator = null, $second = null)
571
    {
572
        return $this->joinSub($query, $as, $first, $operator, $second, 'right');
573
    }
574
575
    /**
576
     * Handle dynamic method calls into the method.
577
     *
578
     * @param  string  $method
579
     * @param  array   $parameters
580
     *
581
     * @return AbstractRepository
582
     *
583
     * @throws BadMethodCallException
584
     */
585
    public function __call($method, $parameters)
586
    {
587
        $pattern = '/^(((where|orWhere).*)|select|limit|groupBy|join|leftJoin|rightJoin|crossJoin)$/';
588
        if (preg_match($pattern, $method)) {
589
            $this->model = call_user_func_array([$this->model, $method], $parameters);
590
            return $this;
591
        }
592
593
        $pattern = '/^(toSql|getBindings)$/';
594
        if (preg_match($pattern, $method)) {
595
            return call_user_func_array([$this->model, $method], $parameters);
596
        }
597
598
        $className = static::class;
599
        throw new BadMethodCallException("Call to undefined method {$className}::{$method}()");
600
    }
601
}
602