Completed
Branch master (411345)
by Rémi
11:20
created

Query   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 645
Duplicated Lines 2.48 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
dl 16
loc 645
rs 8.0135
c 0
b 0
f 0
wmc 51
lcom 1
cbo 10

35 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A get() 0 7 1
A find() 0 10 2
A findMany() 0 10 2
A findOrFail() 8 8 2
A first() 0 4 1
A firstOrFail() 8 8 2
A pluck() 0 8 2
A chunk() 0 15 2
A lists() 0 4 1
A paginate() 0 13 2
A groupedPaginate() 0 6 1
A ungroupedPaginate() 0 13 1
A simplePaginate() 0 10 2
A where() 0 14 2
A orWhere() 0 4 1
A has() 0 14 2
A whereHas() 0 4 1
A orHas() 0 4 1
A orWhereHas() 0 4 1
A addHasWhere() 0 10 2
A mergeWheresToHas() 0 13 1
A getHasRelationQuery() 0 6 1
A getTable() 0 4 1
A with() 0 10 2
A getEagerLoads() 0 4 1
A enforceIdColumn() 0 7 2
A getEntities() 0 15 1
A macro() 0 4 1
A getMacro() 0 4 1
A newQuery() 0 6 1
A newQueryWithoutScopes() 0 4 1
A getMapper() 0 4 1
A getQuery() 0 4 1
A __call() 0 16 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Query often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Query, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Analogue\ORM\System;
4
5
use Closure;
6
use Exception;
7
use Analogue\ORM\EntityCollection;
8
use Analogue\ORM\Relationships\Relationship;
9
use Analogue\ORM\Exceptions\EntityNotFoundException;
10
use Analogue\ORM\Drivers\DBAdapter;
11
use Illuminate\Pagination\Paginator;
12
use Illuminate\Pagination\LengthAwarePaginator;
13
use Illuminate\Database\Query\Expression;
14
15
/**
16
 * Analogue Query builder.
17
 *
18
 * @mixin QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
19
 */
20
class Query
21
{
22
    /**
23
     * Mapper Instance
24
     *
25
     * @var \Analogue\ORM\System\Mapper
26
     */
27
    protected $mapper;
28
29
    /**
30
     * DB Adatper
31
     *
32
     * @var \Analogue\ORM\Drivers\DBAdapter
33
     */
34
    protected $adapter;
35
36
    /**
37
     * Query Builder Instance
38
     *
39
     * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
40
     */
41
    protected $query;
42
43
    /**
44
     * Entity Map Instance
45
     *
46
     * @var \Analogue\ORM\EntityMap
47
     */
48
    protected $entityMap;
49
50
    /**
51
     * The relationships that should be eager loaded.
52
     *
53
     * @var array
54
     */
55
    protected $eagerLoad = [];
56
57
    /**
58
     * All of the registered builder macros.
59
     *
60
     * @var array
61
     */
62
    protected $macros = [];
63
64
    /**
65
     * The methods that should be returned from query builder.
66
     *
67
     * @var array
68
     */
69
    protected $passthru = [
70
        'toSql',
71
        'lists',
72
        'pluck',
73
        'count',
74
        'min',
75
        'max',
76
        'avg',
77
        'sum',
78
        'exists',
79
        'getBindings',
80
    ];
81
82
    /**
83
     * Query Builder Blacklist
84
     */
85
    protected $blacklist = [
86
        'insert',
87
        'insertGetId',
88
        'lock',
89
        'lockForUpdate',
90
        'sharedLock',
91
        'update',
92
        'increment',
93
        'decrement',
94
        'delete',
95
        'truncate',
96
        'raw',
97
    ];
98
99
    /**
100
     * Create a new Analogue Query Builder instance.
101
     *
102
     * @param  Mapper    $mapper
103
     * @param  DBAdapter $adapter
104
     */
105
    public function __construct(Mapper $mapper, DBAdapter $adapter)
106
    {
107
        $this->mapper = $mapper;
108
109
        $this->adapter = $adapter;
110
111
        $this->entityMap = $mapper->getEntityMap();
112
113
        // Specify the table to work on
114
        $this->query = $adapter->getQuery()->from($this->entityMap->getTable());
115
116
        $this->with($this->entityMap->getEagerloadedRelationships());
117
    }
118
119
    /**
120
     * Run the query and return the result
121
     *
122
     * @param  array $columns
123
     * @return \Analogue\ORM\EntityCollection
124
     */
125
    public function get($columns = ['*'])
126
    {
127
        $entities = $this->getEntities($columns);
128
129
        // TODO Should move the call to new Collection on the result builder
130
        return $this->entityMap->newCollection($entities);
0 ignored issues
show
Bug introduced by
It seems like $entities defined by $this->getEntities($columns) on line 127 can also be of type null; however, Analogue\ORM\EntityMap::newCollection() does only seem to accept array, 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...
131
    }
132
133
    /**
134
     * Find an entity by its primary key
135
     *
136
     * @param  string|integer $id
137
     * @param  array          $columns
138
     * @return \Analogue\ORM\Mappable
139
     */
140
    public function find($id, $columns = ['*'])
141
    {
142
        if (is_array($id)) {
143
            return $this->findMany($id, $columns);
144
        }
145
146
        $this->query->where($this->entityMap->getKeyName(), '=', $id);
147
148
        return $this->first($columns);
149
    }
150
151
    /**
152
     * Find many entities by their primary keys.
153
     *
154
     * @param  array $id
155
     * @param  array $columns
156
     * @return EntityCollection
157
     */
158
    public function findMany($id, $columns = ['*'])
159
    {
160
        if (empty($id)) {
161
            return new EntityCollection;
162
        }
163
164
        $this->query->whereIn($this->entityMap->getKeyName(), $id);
165
166
        return $this->get($columns);
167
    }
168
169
    /**
170
     * Find a model by its primary key or throw an exception.
171
     *
172
     * @param  mixed $id
173
     * @param  array $columns
174
     * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
175
     * @return mixed|self
176
     */
177 View Code Duplication
    public function findOrFail($id, $columns = ['*'])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
    {
179
        if (!is_null($entity = $this->find($id, $columns))) {
180
            return $entity;
181
        }
182
183
        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
184
    }
185
186
187
    /**
188
     * Execute the query and get the first result.
189
     *
190
     * @param  array $columns
191
     * @return \Analogue\ORM\Entity
192
     */
193
    public function first($columns = ['*'])
194
    {
195
        return $this->take(1)->get($columns)->first();
0 ignored issues
show
Documentation Bug introduced by
The method take does not exist on object<Analogue\ORM\System\Query>? 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...
196
    }
197
198
    /**
199
     * Execute the query and get the first result or throw an exception.
200
     *
201
     * @param  array $columns
202
     * @throws EntityNotFoundException
203
     * @return \Analogue\ORM\Entity
204
     */
205 View Code Duplication
    public function firstOrFail($columns = ['*'])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
    {
207
        if (!is_null($entity = $this->first($columns))) {
208
            return $entity;
209
        }
210
211
        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
212
    }
213
214
    /**
215
     * Pluck a single column from the database.
216
     *
217
     * @param  string $column
218
     * @return mixed
219
     */
220
    public function pluck($column)
221
    {
222
        $result = $this->first([$column]);
223
224
        if ($result) {
225
            return $result->{$column};
226
        }
227
    }
228
229
    /**
230
     * Chunk the results of the query.
231
     *
232
     * @param  int      $count
233
     * @param  callable $callback
234
     * @return void
235
     */
236
    public function chunk($count, callable $callback)
237
    {
238
        $results = $this->forPage($page = 1, $count)->get();
0 ignored issues
show
Documentation Bug introduced by
The method forPage does not exist on object<Analogue\ORM\System\Query>? 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...
239
240
        while (count($results) > 0) {
241
            // On each chunk result set, we will pass them to the callback and then let the
242
            // developer take care of everything within the callback, which allows us to
243
            // keep the memory low for spinning through large result sets for working.
244
            call_user_func($callback, $results);
245
246
            $page++;
247
248
            $results = $this->forPage($page, $count)->get();
0 ignored issues
show
Documentation Bug introduced by
The method forPage does not exist on object<Analogue\ORM\System\Query>? 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...
249
        }
250
    }
251
252
    /**
253
     * Get an array with the values of a given column.
254
     *
255
     * @param  string $column
256
     * @param  string $key
257
     * @return array
258
     */
259
    public function lists($column, $key = null)
260
    {
261
        return $this->query->pluck($column, $key);
262
    }
263
264
    /**
265
     * Get a paginator for the "select" statement.
266
     *
267
     * @param  int   $perPage
268
     * @param  array $columns
269
     * @return LengthAwarePaginator
270
     */
271
    public function paginate($perPage = null, $columns = ['*'])
272
    {
273
        $total = $this->query->getCountForPagination();
274
275
        $this->query->forPage(
276
            $page = Paginator::resolveCurrentPage(),
277
            $perPage = $perPage ?: $this->entityMap->getPerPage()
278
        );
279
280
        return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
281
            'path' => Paginator::resolveCurrentPath()
282
        ]);
283
    }
284
285
    /**
286
     * Get a paginator for a grouped statement.
287
     *
288
     * @param  \Illuminate\Pagination\Factory $paginator
289
     * @param  int                            $perPage
290
     * @param  array                          $columns
291
     * @return \Illuminate\Pagination\Paginator
292
     */
293
    protected function groupedPaginate($paginator, $perPage, $columns)
294
    {
295
        $results = $this->get($columns)->all();
296
297
        return $this->query->buildRawPaginator($paginator, $results, $perPage);
298
    }
299
300
    /**
301
     * Get a paginator for an ungrouped statement.
302
     *
303
     * @param  \Illuminate\Pagination\Factory $paginator
304
     * @param  int                            $perPage
305
     * @param  array                          $columns
306
     * @return \Illuminate\Pagination\Paginator
307
     */
308
    protected function ungroupedPaginate($paginator, $perPage, $columns)
309
    {
310
        $total = $this->query->getPaginationCount();
311
312
        // Once we have the paginator we need to set the limit and offset values for
313
        // the query so we can get the properly paginated items. Once we have an
314
        // array of items we can create the paginator instances for the items.
315
        $page = $paginator->getCurrentPage($total);
316
317
        $this->query->forPage($page, $perPage);
318
319
        return $paginator->make($this->get($columns)->all(), $total, $perPage);
320
    }
321
322
    /**
323
     * Paginate the given query into a simple paginator.
324
     *
325
     * @param  int   $perPage
326
     * @param  array $columns
327
     * @return \Illuminate\Contracts\Pagination\Paginator
328
     */
329
    public function simplePaginate($perPage = null, $columns = ['*'])
330
    {
331
        $page = Paginator::resolveCurrentPage();
332
333
        $perPage = $perPage ?: $this->entityMap->getPerPage();
334
335
        $this->skip(($page - 1) * $perPage)->take($perPage + 1);
0 ignored issues
show
Documentation Bug introduced by
The method skip does not exist on object<Analogue\ORM\System\Query>? 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...
336
337
        return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
338
    }
339
340
    /**
341
     * Add a basic where clause to the query.
342
     *
343
     * @param  string $column
344
     * @param  string $operator
345
     * @param  mixed  $value
346
     * @param  string $boolean
347
     * @return $this
348
     */
349
    public function where($column, $operator = null, $value = null, $boolean = 'and')
0 ignored issues
show
Unused Code introduced by
The parameter $operator is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
350
    {
351
        if ($column instanceof Closure) {
352
            $query = $this->newQueryWithoutScopes();
353
354
            call_user_func($column, $query);
355
356
            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
357
        } else {
358
            call_user_func_array([$this->query, 'where'], func_get_args());
359
        }
360
361
        return $this;
362
    }
363
364
    /**
365
     * Add an "or where" clause to the query.
366
     *
367
     * @param  string $column
368
     * @param  string $operator
369
     * @param  mixed  $value
370
     * @return \Analogue\ORM\System\Query
371
     */
372
    public function orWhere($column, $operator = null, $value = null)
373
    {
374
        return $this->where($column, $operator, $value, 'or');
375
    }
376
377
    /**
378
     * Add a relationship count condition to the query.
379
     *
380
     * @param  string   $relation
381
     * @param  string   $operator
382
     * @param  int      $count
383
     * @param  string   $boolean
384
     * @param  \Closure $callback
385
     * @return \Analogue\ORM\System\Query
386
     */
387
    public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
388
    {
389
        $entity = $this->mapper->newInstance();
390
391
        $relation = $this->getHasRelationQuery($relation, $entity);
392
393
        $query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
0 ignored issues
show
Documentation Bug introduced by
The method getRelatedMapper does not exist on object<Analogue\ORM\System\Query>? 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...
Documentation Bug introduced by
The method getRelationCountQuery does not exist on object<Analogue\ORM\System\Query>? 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...
394
395
        if ($callback) {
396
            call_user_func($callback, $query);
397
        }
398
399
        return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
0 ignored issues
show
Documentation introduced by
$relation is of type object<Analogue\ORM\System\Query>, but the function expects a object<Analogue\ORM\Relationships\Relationship>.

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...
400
    }
401
402
    /**
403
     * Add a relationship count condition to the query with where clauses.
404
     *
405
     * @param  string   $relation
406
     * @param  \Closure $callback
407
     * @param  string   $operator
408
     * @param  int      $count
409
     * @return \Analogue\ORM\System\Query
410
     */
411
    public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
412
    {
413
        return $this->has($relation, $operator, $count, 'and', $callback);
414
    }
415
416
    /**
417
     * Add a relationship count condition to the query with an "or".
418
     *
419
     * @param  string $relation
420
     * @param  string $operator
421
     * @param  int    $count
422
     * @return \Analogue\ORM\System\Query
423
     */
424
    public function orHas($relation, $operator = '>=', $count = 1)
425
    {
426
        return $this->has($relation, $operator, $count, 'or');
427
    }
428
429
    /**
430
     * Add a relationship count condition to the query with where clauses and an "or".
431
     *
432
     * @param  string   $relation
433
     * @param  \Closure $callback
434
     * @param  string   $operator
435
     * @param  int      $count
436
     * @return \Analogue\ORM\System\Query
437
     */
438
    public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
439
    {
440
        return $this->has($relation, $operator, $count, 'or', $callback);
441
    }
442
443
    /**
444
     * Add the "has" condition where clause to the query.
445
     *
446
     * @param  \Analogue\ORM\System\Query               $hasQuery
447
     * @param  \Analogue\ORM\Relationships\Relationship $relation
448
     * @param  string                                   $operator
449
     * @param  int                                      $count
450
     * @param  string                                   $boolean
451
     * @return \Analogue\ORM\System\Query
452
     */
453
    protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
454
    {
455
        $this->mergeWheresToHas($hasQuery, $relation);
456
457
        if (is_numeric($count)) {
458
            $count = new Expression($count);
459
        }
460
461
        return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
0 ignored issues
show
Documentation Bug introduced by
The method toSql does not exist on object<Analogue\ORM\System\Query>? 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...
462
    }
463
464
    /**
465
     * Merge the "wheres" from a relation query to a has query.
466
     *
467
     * @param  \Analogue\ORM\System\Query               $hasQuery
468
     * @param  \Analogue\ORM\Relationships\Relationship $relation
469
     * @return void
470
     */
471
    protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
472
    {
473
        // Here we have the "has" query and the original relation. We need to copy over any
474
        // where clauses the developer may have put in the relationship function over to
475
        // the has query, and then copy the bindings from the "has" query to the main.
476
        $relationQuery = $relation->getBaseQuery();
477
478
        $hasQuery->mergeWheres(
0 ignored issues
show
Bug introduced by
The method mergeWheres() does not exist on Analogue\ORM\System\Query. Did you maybe mean mergeWheresToHas()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
479
            $relationQuery->wheres, $relationQuery->getBindings()
480
        );
481
482
        $this->query->mergeBindings($hasQuery->getQuery());
483
    }
484
485
    /**
486
     * Get the "has relation" base query instance.
487
     *
488
     * @param  string $relation
489
     * @param         $entity
490
     * @return \Analogue\ORM\System\Query
491
     */
492
    protected function getHasRelationQuery($relation, $entity)
493
    {
494
        return Relationship::noConstraints(function () use ($relation, $entity) {
495
            return $this->entityMap->$relation($entity);
496
        });
497
    }
498
499
    /**
500
     * Get the table for the current query object
501
     *
502
     * @return string
503
     */
504
    public function getTable()
505
    {
506
        return $this->entityMap->getTable();
507
    }
508
509
    /**
510
     * Set the relationships that should be eager loaded.
511
     *
512
     * @param  mixed $relations
513
     * @return $this
514
     */
515
    public function with($relations)
516
    {
517
        if (is_string($relations)) {
518
            $relations = func_get_args();
519
        }
520
521
        $this->eagerLoad = array_merge($this->eagerLoad, $relations);
522
523
        return $this;
524
    }
525
526
    /**
527
     * Get the relationships being eagerly loaded.
528
     *
529
     * @return array
530
     */
531
    public function getEagerLoads()
532
    {
533
        return $this->eagerLoad;
534
    }
535
536
    /**
537
     * Add the Entity primary key if not in requested columns
538
     *
539
     * @param  array $columns
540
     * @return array
541
     */
542
    protected function enforceIdColumn($columns)
543
    {
544
        if (!in_array($this->entityMap->getKeyName(), $columns)) {
545
            $columns[] = $this->entityMap->getKeyName();
546
        }
547
        return $columns;
548
    }
549
550
    /**
551
     * Get the hydrated models without eager loading.
552
     *
553
     * @param  array  $columns
554
     * @return \Analogue\ORM\EntityCollection
555
     */
556
    public function getEntities($columns = ['*'])
557
    {
558
        // As we need the primary key to feed the
559
        // entity cache, we need it loaded on each
560
        // request
561
        $columns = $this->enforceIdColumn($columns);
562
563
        // Run the query
564
        $results = $this->query->get($columns)->toArray();
565
566
        // Create a result builder.
567
        $builder = new ResultBuilder($this->mapper); 
568
569
        return $builder->build($results, $this->getEagerLoads());
570
    }
571
572
    /**
573
     * Extend the builder with a given callback.
574
     *
575
     * @param  string   $name
576
     * @param  \Closure $callback
577
     * @return void
578
     */
579
    public function macro($name, Closure $callback)
580
    {
581
        $this->macros[$name] = $callback;
582
    }
583
584
    /**
585
     * Get the given macro by name.
586
     *
587
     * @param  string $name
588
     * @return \Closure
589
     */
590
    public function getMacro($name)
591
    {
592
        return array_get($this->macros, $name);
593
    }
594
595
    /**
596
     * Get a new query builder for the model's table.
597
     *
598
     * @return \Analogue\ORM\System\Query
599
     */
600
    public function newQuery()
601
    {
602
        $builder = new Query($this->mapper, $this->adapter);
603
604
        return $this->applyGlobalScopes($builder);
0 ignored issues
show
Documentation Bug introduced by
The method applyGlobalScopes does not exist on object<Analogue\ORM\System\Query>? 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...
605
    }
606
607
    /**
608
     * Get a new query builder without any scope applied.
609
     *
610
     * @return \Analogue\ORM\System\Query
611
     */
612
    public function newQueryWithoutScopes()
613
    {
614
        return new Query($this->mapper, $this->adapter);
615
    }
616
617
    /**
618
     * Get the Mapper instance for this Query Builder
619
     *
620
     * @return \Analogue\ORM\System\Mapper
621
     */
622
    public function getMapper()
623
    {
624
        return $this->mapper;
625
    }
626
627
    /**
628
     * Get the underlying query adapter
629
     *
630
     * (REFACTOR: this method should move out, we need to provide the client classes
631
     * with the adapter instead.)
632
     *
633
     * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
634
     */
635
    public function getQuery()
636
    {
637
        return $this->query;
638
    }
639
640
    /**
641
     * Dynamically handle calls into the query instance.
642
     *
643
     * @param  string $method
644
     * @param  array  $parameters
645
     * @throws Exception
646
     * @return mixed
647
     */
648
    public function __call($method, $parameters)
649
    {
650
        if (isset($this->macros[$method])) {
651
            array_unshift($parameters, $this);
652
653
            return call_user_func_array($this->macros[$method], $parameters);
654
        }
655
656
        if (in_array($method, $this->blacklist)) {
657
            throw new Exception("Method $method doesn't exist");
658
        }
659
660
        $result = call_user_func_array([$this->query, $method], $parameters);
661
662
        return in_array($method, $this->passthru) ? $result : $this;
663
    }
664
}