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); |
|
|
|
|
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)); |
|
|
|
|
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) { |
|
|
|
|
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
|
|
|
|
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.