Completed
Push — master ( 56d3b9...264ef8 )
by Jared
01:47
created

Query::execute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * @author Jared King <[email protected]>
5
 *
6
 * @see http://jaredtking.com
7
 *
8
 * @copyright 2015 Jared King
9
 * @license MIT
10
 */
11
12
namespace Pulsar;
13
14
use Pulsar\Relation\Relationship;
15
16
/**
17
 * Represents a query against a model type.
18
 */
19
class Query
20
{
21
    const DEFAULT_LIMIT = 100;
22
    const MAX_LIMIT = 1000;
23
24
    /**
25
     * @var Model|string
26
     */
27
    private $model;
28
29
    /**
30
     * @var array
31
     */
32
    private $joins;
33
34
    /**
35
     * @var array
36
     */
37
    private $eagerLoaded;
38
39
    /**
40
     * @var array
41
     */
42
    private $where;
43
44
    /**
45
     * @var int
46
     */
47
    private $limit;
48
49
    /**
50
     * @var int
51
     */
52
    private $start;
53
54
    /**
55
     * @var array
56
     */
57
    private $sort;
58
59
    /**
60
     * @param Model|string $model
61
     */
62
    public function __construct($model = '')
63
    {
64
        $this->model = $model;
65
        $this->joins = [];
66
        $this->eagerLoaded = [];
67
        $this->where = [];
68
        $this->start = 0;
69
        $this->limit = self::DEFAULT_LIMIT;
70
        $this->sort = [];
71
    }
72
73
    /**
74
     * Gets the model class associated with this query.
75
     *
76
     * @return Model|string
77
     */
78
    public function getModel()
79
    {
80
        return $this->model;
81
    }
82
83
    /**
84
     * Sets the limit for this query.
85
     *
86
     * @return $this
87
     */
88
    public function limit(int $limit)
89
    {
90
        $this->limit = min($limit, self::MAX_LIMIT);
91
92
        return $this;
93
    }
94
95
    /**
96
     * Gets the limit for this query.
97
     */
98
    public function getLimit(): int
99
    {
100
        return $this->limit;
101
    }
102
103
    /**
104
     * Sets the start offset.
105
     *
106
     * @return $this
107
     */
108
    public function start(int $start)
109
    {
110
        $this->start = max($start, 0);
111
112
        return $this;
113
    }
114
115
    /**
116
     * Gets the start offset.
117
     */
118
    public function getStart(): int
119
    {
120
        return $this->start;
121
    }
122
123
    /**
124
     * Sets the sort pattern for the query.
125
     *
126
     * @param array|string $sort
127
     *
128
     * @return $this
129
     */
130
    public function sort($sort)
131
    {
132
        $columns = explode(',', $sort);
133
134
        $sortParams = [];
135
        foreach ($columns as $column) {
136
            $c = explode(' ', trim($column));
137
138
            if (2 != count($c)) {
139
                continue;
140
            }
141
142
            // validate direction
143
            $direction = strtolower($c[1]);
144
            if (!in_array($direction, ['asc', 'desc'])) {
145
                continue;
146
            }
147
148
            $sortParams[] = [$c[0], $direction];
149
        }
150
151
        $this->sort = $sortParams;
152
153
        return $this;
154
    }
155
156
    /**
157
     * Gets the sort parameters.
158
     */
159
    public function getSort(): array
160
    {
161
        return $this->sort;
162
    }
163
164
    /**
165
     * Sets the where parameters.
166
     * Accepts the following forms:
167
     *   i)   where(['name' => 'Bob'])
168
     *   ii)  where('name', 'Bob')
169
     *   iii) where('balance', 100, '>')
170
     *   iv)  where('balance > 100').
171
     *
172
     * @param array|string $where
173
     * @param mixed        $value     optional value
174
     * @param string|null  $condition optional condition
175
     *
176
     * @return $this
177
     */
178
    public function where($where, $value = null, $condition = null)
179
    {
180
        // handles i.
181
        if (is_array($where)) {
182
            $this->where = array_merge($this->where, $where);
183
        } else {
184
            // handles iii.
185
            $args = func_num_args();
186
            if ($args > 2) {
187
                $this->where[] = [$where, $value, $condition];
188
            // handles ii.
189
            } elseif (2 == $args) {
190
                $this->where[$where] = $value;
191
            // handles iv.
192
            } else {
193
                $this->where[] = $where;
194
            }
195
        }
196
197
        return $this;
198
    }
199
200
    /**
201
     * Gets the where parameters.
202
     */
203
    public function getWhere(): array
204
    {
205
        return $this->where;
206
    }
207
208
    /**
209
     * Adds a join to the query. Matches a property on this model
210
     * to the ID of the model we are joining.
211
     *
212
     * @param string $model  model being joined
213
     * @param string $column name of local property
214
     *
215
     * @return $this
216
     */
217
    public function join($model, string $column, string $foreignKey)
218
    {
219
        $this->joins[] = [$model, $column, $foreignKey];
220
221
        return $this;
222
    }
223
224
    /**
225
     * Gets the joins.
226
     */
227
    public function getJoins(): array
228
    {
229
        return $this->joins;
230
    }
231
232
    /**
233
     * Marks a relationship property on the model that should be eager loaded.
234
     *
235
     * @param string $k local property containing the relationship
236
     *
237
     * @return $this
238
     */
239
    public function with(string $k)
240
    {
241
        if (!in_array($k, $this->eagerLoaded)) {
242
            $this->eagerLoaded[] = $k;
243
        }
244
245
        return $this;
246
    }
247
248
    /**
249
     * Gets the relationship properties that are going to be eager-loaded.
250
     */
251
    public function getWith(): array
252
    {
253
        return $this->eagerLoaded;
254
    }
255
256
    /**
257
     * Executes the query against the model's driver.
258
     *
259
     * @return array results
260
     */
261
    public function execute(): array
262
    {
263
        // instantiate a model so that initialize() is called and properties are filled in
264
        // otherwise this empty model is not used
265
        $modelClass = $this->model;
266
        $model = new $modelClass();
0 ignored issues
show
Unused Code introduced by
$model is not used, you could remove the assignment.

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

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

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

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

Loading history...
267
268
        return Hydrator::hydrate($modelClass::getDriver()->queryModels($this), $modelClass, $this->eagerLoaded);
269
    }
270
271
    /**
272
     * Creates an iterator for a search.
273
     *
274
     * @return Iterator
275
     */
276
    public function all()
277
    {
278
        return new Iterator($this);
279
    }
280
281
    /**
282
     * Executes the query against the model's driver and returns the first result.
283
     *
284
     * @return array|Model|null when $limit = 1, returns a single model or null, otherwise returns an array
285
     */
286
    public function first(int $limit = 1)
287
    {
288
        $models = $this->limit($limit)->execute();
289
290
        if (1 == $limit) {
291
            return (1 == count($models)) ? $models[0] : null;
292
        }
293
294
        return $models;
295
    }
296
297
    /**
298
     * Gets the number of models matching the query.
299
     */
300
    public function count(): int
301
    {
302
        $model = $this->model;
303
        $driver = $model::getDriver();
304
305
        return $driver->count($this);
306
    }
307
308
    /**
309
     * Gets the sum of a property matching the query.
310
     *
311
     * @return number
312
     */
313
    public function sum(string $property)
314
    {
315
        $model = $this->model;
316
        $driver = $model::getDriver();
317
318
        return $driver->sum($this, $property);
319
    }
320
321
    /**
322
     * Gets the average of a property matching the query.
323
     *
324
     * @return number
325
     */
326
    public function average(string $property)
327
    {
328
        $model = $this->model;
329
        $driver = $model::getDriver();
330
331
        return $driver->average($this, $property);
332
    }
333
334
    /**
335
     * Gets the max of a property matching the query.
336
     *
337
     * @return number
338
     */
339
    public function max(string $property)
340
    {
341
        $model = $this->model;
342
        $driver = $model::getDriver();
343
344
        return $driver->max($this, $property);
345
    }
346
347
    /**
348
     * Gets the min of a property matching the query.
349
     *
350
     * @return number
351
     */
352
    public function min(string $property)
353
    {
354
        $model = $this->model;
355
        $driver = $model::getDriver();
356
357
        return $driver->min($this, $property);
358
    }
359
360
    /**
361
     * Updates all of the models matched by this query.
362
     *
363
     * @todo should be optimized to be done in a single call to the data layer
364
     *
365
     * @param array $params key-value update parameters
366
     *
367
     * @return int # of models updated
368
     */
369
    public function set(array $params): int
370
    {
371
        $n = 0;
372
        foreach ($this->all() as $model) {
373
            $model->set($params);
374
            ++$n;
375
        }
376
377
        return $n;
378
    }
379
380
    /**
381
     * Deletes all of the models matched by this query.
382
     *
383
     * @todo should be optimized to be done in a single call to the data layer
384
     *
385
     * @return int # of models deleted
386
     */
387
    public function delete(): int
388
    {
389
        $n = 0;
390
        foreach ($this->all() as $model) {
391
            $model->delete();
392
            ++$n;
393
        }
394
395
        return $n;
396
    }
397
}
398