Completed
Push — master ( 9a7d30...436403 )
by Sherif
49s
created
src/Modules/Core/BaseClasses/BaseRepository.php 2 patches
Indentation   +460 added lines, -460 removed lines patch added patch discarded remove patch
@@ -14,464 +14,464 @@
 block discarded – undo
14 14
 
15 15
 abstract class BaseRepository implements BaseRepositoryInterface
16 16
 {
17
-    /**
18
-     * @var Model
19
-     */
20
-    public $model;
21
-
22
-    /**
23
-     * Init new object.
24
-     *
25
-     * @param   Model$model
26
-     * @return  void
27
-     */
28
-    public function __construct(Model $model)
29
-    {
30
-        $this->model = $model;
31
-    }
32
-
33
-    /**
34
-     * Fetch all records with relations from the storage.
35
-     *
36
-     * @param  array  $relations
37
-     * @param  string $sortBy
38
-     * @param  bool   $desc
39
-     * @param  array  $columns
40
-     * @return collection
41
-     */
42
-    public function all(array $relations = [], string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): Collection
43
-    {
44
-        $sort = $desc ? 'desc' : 'asc';
45
-        return $this->model->with($relations)->orderBy($sortBy, $sort)->get($columns);
46
-    }
47
-
48
-    /**
49
-     * Fetch all records with relations from storage in pages.
50
-     *
51
-     * @param  int    $perPage
52
-     * @param  array  $relations
53
-     * @param  string $sortBy
54
-     * @param  bool   $desc
55
-     * @param  array  $columns
56
-     * @return LengthAwarePaginator
57
-     */
58
-    public function paginate(int $perPage = 15, array $relations = [], string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): LengthAwarePaginator
59
-    {
60
-        $sort = $desc ? 'desc' : 'asc';
61
-        return $this->model->with($relations)->orderBy($sortBy, $sort)->paginate($perPage, $columns);
62
-    }
63
-
64
-    /**
65
-     * Fetch all records with relations based on
66
-     * the given condition from storage in pages.
67
-     *
68
-     * @param  array  $conditions array of conditions
69
-     * @param  int    $perPage
70
-     * @param  array  $relations
71
-     * @param  string $sortBy
72
-     * @param  bool   $desc
73
-     * @param  array  $columns
74
-     * @return LengthAwarePaginator
75
-     */
76
-    public function paginateBy(array $conditions, int $perPage = 15, array $relations = [], string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): LengthAwarePaginator
77
-    {
78
-        $conditions = $this->constructConditions($conditions, $this->model);
79
-        $sort       = $desc ? 'desc' : 'asc';
80
-        return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->paginate($perPage, $columns);
81
-    }
82
-
83
-    /**
84
-     * Count all records based on the given condition from storage.
85
-     *
86
-     * @param  array $conditions array of conditions
87
-     * @return int
88
-     */
89
-    public function count(array $conditions = []): int
90
-    {
91
-        if ($conditions) {
92
-            $conditions = $this->constructConditions($conditions, $this->model);
93
-            return $this->model->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->count();
94
-        }
95
-
96
-        return $this->model->count();
97
-    }
98
-
99
-    /**
100
-     * Pluck column based on the given condition from storage.
101
-     *
102
-     * @param  array  $conditions array of conditions
103
-     * @param  string $column
104
-     * @return collection
105
-     */
106
-    public function pluck(array $conditions, string $column): Collection
107
-    {
108
-        $conditions = $this->constructConditions($conditions, $this->model);
109
-        return $this->model->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->pluck($column);
110
-    }
111
-
112
-    /**
113
-     * Save the given model to the storage.
114
-     *
115
-     * @param  array $data
116
-     * @return Model
117
-     */
118
-    public function save(array $data): Model
119
-    {
120
-        $model     = new Model();
121
-        $relations = [];
122
-
123
-        DB::transaction(function () use (&$model, &$relations, $data) {
124
-
125
-            $model = $this->prepareModel($data);
126
-            $model->save();
127
-
128
-            $relations = $this->prepareManyToManyRelations($data, $model);
129
-            $this->saveManyToManyRelation($model, $relations);
130
-        });
131
-
132
-        if (count($relations)) {
133
-            $model->load(...array_keys($relations));
134
-        }
135
-
136
-        return $model;
137
-    }
138
-
139
-    /**
140
-     * Insert the given model/models to the storage.
141
-     *
142
-     * @param  array $data
143
-     * @return bool
144
-     */
145
-    public function insert(array $data): bool
146
-    {
147
-        return $this->model->insert($data);
148
-    }
149
-
150
-    /**
151
-     * Delete record from the storage based on the given
152
-     * condition.
153
-     *
154
-     * @param  string $value condition value
155
-     * @param  string $attribute condition column name
156
-     * @return bool
157
-     */
158
-    public function delete(string $value, string $attribute = 'id'): bool
159
-    {
160
-        DB::transaction(function () use ($value, $attribute) {
161
-            $this->model->where($attribute, '=', $value)->lockForUpdate()->get()->each(function ($model) {
162
-                $model->delete();
163
-            });
164
-        });
165
-
166
-        return true;
167
-    }
168
-
169
-    /**
170
-     * Fetch records from the storage based on the given
171
-     * id.
172
-     *
173
-     * @param  int   $id
174
-     * @param  array $relations
175
-     * @param  array $columns
176
-     * @return Model
177
-     */
178
-    public function find(int $id, array $relations = [], array $columns = ['*']): Model
179
-    {
180
-        return $this->model->with($relations)->find($id, $columns);
181
-    }
182
-
183
-    /**
184
-     * Fetch records from the storage based on the given
185
-     * condition.
186
-     *
187
-     * @param  array   $conditions array of conditions
188
-     * @param  array   $relations
189
-     * @param  string  $sortBy
190
-     * @param  bool    $desc
191
-     * @param  array   $columns
192
-     * @return collection
193
-     */
194
-    public function findBy(array $conditions, array $relations = [], string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): Collection
195
-    {
196
-        $conditions = $this->constructConditions($conditions, $this->model);
197
-        $sort       = $desc ? 'desc' : 'asc';
198
-        return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->get($columns);
199
-    }
200
-
201
-    /**
202
-     * Fetch the first record from the storage based on the given
203
-     * condition.
204
-     *
205
-     * @param  array $conditions array of conditions
206
-     * @param  array $relations
207
-     * @param  array $columns
208
-     * @return Model
209
-     */
210
-    public function first(array $conditions, array $relations = [], array $columns = ['*']): Model
211
-    {
212
-        $conditions = $this->constructConditions($conditions, $this->model);
213
-        return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->first($columns);
214
-    }
215
-
216
-    /**
217
-     * Return the deleted models in pages based on the given conditions.
218
-     *
219
-     * @param  array  $conditions array of conditions
220
-     * @param  int    $perPage
221
-     * @param  string $sortBy
222
-     * @param  bool   $desc
223
-     * @param  array  $columns
224
-     * @return LengthAwarePaginator
225
-     */
226
-    public function deleted(array $conditions, int $perPage = 15, string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): LengthAwarePaginator
227
-    {
228
-        unset($conditions['page'], $conditions['perPage'], $conditions['sortBy'], $conditions['sort']);
229
-        $conditions = $this->constructConditions($conditions, $this->model);
230
-        $sort       = $desc ? 'desc' : 'asc';
231
-        $model      = $this->model->onlyTrashed();
232
-
233
-        if (count($conditions['conditionValues'])) {
234
-            $model->whereRaw($conditions['conditionString'], $conditions['conditionValues']);
235
-        }
236
-
237
-        return $model->orderBy($sortBy, $sort)->paginate($perPage, $columns);
238
-    }
239
-
240
-    /**
241
-     * Restore the deleted model.
242
-     *
243
-     * @param  int $id
244
-     * @return bool
245
-     */
246
-    public function restore(int $id): bool
247
-    {
248
-        $model = $this->model->onlyTrashed()->find($id);
249
-
250
-        if (!$model) {
251
-            Errors::notFound(class_basename($this->model) . ' with id : ' . $id);
252
-        }
253
-
254
-        $model->restore();
255
-
256
-        return true;
257
-    }
258
-
259
-    /**
260
-     * Fill the model with the given data.
261
-     *
262
-     * @param   array  $data
263
-     * @return  Model
264
-     */
265
-    protected function prepareModel(array $data): Model
266
-    {
267
-        $modelClass = $this->model;
268
-        $model = Arr::has($data, 'id') ? $modelClass->lockForUpdate()->find($data['id']) : new $modelClass;
269
-
270
-        if (!$model) {
271
-            Errors::notFound(class_basename($modelClass) . ' with id : ' . $data['id']);
272
-        }
273
-
274
-        foreach ($data as $key => $value) {
275
-            if (array_search($key, $model->getFillable(), true) !== false) {
276
-                $model->$key = $value;
277
-            }
278
-        }
279
-
280
-        return $model;
281
-    }
282
-
283
-    /**
284
-     * Prepare many to many relation if found.
285
-     *
286
-     * @param   array $data
287
-     * @param   Model $model
288
-     *
289
-     * @return  array
290
-     */
291
-    protected function prepareManyToManyRelations(array $data, Model $model): array
292
-    {
293
-        $relations = [];
294
-        foreach ($data as $key => $relatedModels) {
295
-            $relation = Str::camel($key);
296
-            if (method_exists($model, $relation) &&
297
-                Core::$relation() &&
298
-                in_array(class_basename($model->$key()), ['MorphToMany', 'BelongsToMany'])
299
-            ) {
300
-                if (!$relatedModels || !count($relatedModels)) {
301
-                    /**
302
-                     * Mark the relation to be deleted.
303
-                     */
304
-                    $relations[$relation] = 'delete';
305
-                }
306
-
307
-                foreach ($relatedModels as $relatedModel) {
308
-                    $relationBaseModel = Core::$relation()->model;
309
-                    $relations[$relation][] = $this->prepareRelatedModel($relatedModel, $relationBaseModel);
310
-                }
311
-            }
312
-        }
313
-
314
-        return $relations;
315
-    }
316
-
317
-    /**
318
-     * Prepare related models with extra values for pivot if found.
319
-     *
320
-     * @param   mixed  $relatedModelData
321
-     * @param   Model  $relationBaseModel
322
-     *
323
-     * @return  Model
324
-     */
325
-    protected function prepareRelatedModel(mixed $relatedModelData, Model $relationBaseModel): Model
326
-    {
327
-        if (!is_array($relatedModelData)) { // if the relation is integer id
328
-            $relatedModel = $relationBaseModel->lockForUpdate()->find($relatedModelData);
329
-        } else {
330
-            $relatedModel = Arr::has($relatedModelData, 'id') ? $relationBaseModel->lockForUpdate()->find($relatedModelData['id']) : new $relationBaseModel;
331
-        }
332
-
333
-        if (!$relatedModel) {
334
-            Errors::notFound(class_basename($relationBaseModel) . ' with id : ' . $relatedModelData['id']);
335
-        }
336
-
337
-        if (is_array($relatedModelData)) {
338
-            foreach ($relatedModelData as $attr => $val) {
339
-                if (array_search($attr, $relatedModel->getFillable(), true) === false &&
340
-                    gettype($val) !== 'object' &&
341
-                    gettype($val) !== 'array' &&
342
-                    $attr !== 'id'
343
-                ) {
344
-                    $extra[$attr] = $val;
345
-                }
346
-            }
347
-        }
348
-
349
-        if (isset($extra)) {
350
-            $relatedModel->extra = $extra;
351
-        }
352
-
353
-        return $relatedModel;
354
-    }
355
-
356
-    /**
357
-     * Save the given model many to many relations.
358
-     *
359
-     * @param   Model $model
360
-     * @param   array $relations
361
-     *
362
-     * @return  void
363
-     */
364
-    protected function saveManyToManyRelation(Model $model, array $relations)
365
-    {
366
-        foreach ($relations as $key => $value) {
367
-            /**
368
-             * If the relation is marked for delete then delete it.
369
-             */
370
-            if ($value == 'delete' && $model->$key()->count()) {
371
-                $model->$key()->detach();
372
-            } else {
373
-                $ids = [];
374
-                foreach ($value as $val) {
375
-                    $extra = $val->extra;
376
-                    $ids[$val->id] = $extra ?? [];
377
-                    unset($val->extra);
378
-                }
379
-
380
-                $model->$key()->detach();
381
-                $model->$key()->attach($ids);
382
-            }
383
-        }
384
-    }
385
-
386
-    /**
387
-     * Build the conditions recursively for the retrieving methods.
388
-     *
389
-     * @param  array $conditions
390
-     * @param  Model $model
391
-     * @return array
392
-     */
393
-    protected function constructConditions(array $conditions, Model $model): array
394
-    {
395
-        $conditionString = '';
396
-        $conditionValues = [];
397
-        foreach ($conditions as $key => $value) {
398
-            if (Str::contains($key, '->')) {
399
-                $key = $this->wrapJsonSelector($key);
400
-            }
401
-
402
-            if ($key == 'and') {
403
-                $conditions       = $this->constructConditions($value, $model);
404
-                $conditionString .= str_replace('{op}', 'and', $conditions['conditionString']) . ' {op} ';
405
-                $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
406
-            } elseif ($key == 'or') {
407
-                $conditions       = $this->constructConditions($value, $model);
408
-                $conditionString .= str_replace('{op}', 'or', $conditions['conditionString']) . ' {op} ';
409
-                $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
410
-            } else {
411
-                if (is_array($value)) {
412
-                    $operator = $value['op'];
413
-                    if (strtolower($operator) == 'between') {
414
-                        $value1 = $value['val1'];
415
-                        $value2 = $value['val2'];
416
-                    } else {
417
-                        $value = Arr::get($value, 'val', '');
418
-                    }
419
-                } else {
420
-                    $operator = '=';
421
-                }
422
-
423
-                if (strtolower($operator) == 'between') {
424
-                    $conditionString  .= $key . ' >= ? and ';
425
-                    $conditionValues[] = $value1;
426
-
427
-                    $conditionString  .= $key . ' <= ? {op} ';
428
-                    $conditionValues[] = $value2;
429
-                } elseif (strtolower($operator) == 'in') {
430
-                    $conditionValues  = array_merge($conditionValues, $value);
431
-                    $inBindingsString = rtrim(str_repeat('?,', count($value)), ',');
432
-                    $conditionString .= $key . ' in (' . rtrim($inBindingsString, ',') . ') {op} ';
433
-                } elseif (strtolower($operator) == 'null') {
434
-                    $conditionString .= $key . ' is null {op} ';
435
-                } elseif (strtolower($operator) == 'not null') {
436
-                    $conditionString .= $key . ' is not null {op} ';
437
-                } elseif (strtolower($operator) == 'has') {
438
-                    $sql              = $model->withTrashed()->withoutGlobalScopes()->has($key)->toSql();
439
-                    $bindings         = $model->withTrashed()->withoutGlobalScopes()->has($key)->getBindings();
440
-                    if ($value) {
441
-                        $conditions       = $this->constructConditions($value, $model->$key()->getRelated());
442
-                        $conditionString .= substr(substr($sql, strpos($sql, 'exists')), 0, -1) . ' and ' . $conditions['conditionString'] . ') {op} ';
443
-                        $conditionValues  = array_merge($conditionValues, $bindings);
444
-                        $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
445
-                    } else {
446
-                        $conditionString .= substr(substr($sql, strpos($sql, 'exists')), 0, -1) . ') {op} ';
447
-                        $conditionValues  = array_merge($conditionValues, $bindings);
448
-                    }
449
-                } else {
450
-                    $conditionString  .= $key . ' ' . $operator . ' ? {op} ';
451
-                    $conditionValues[] = $value;
452
-                }
453
-            }
454
-        }
455
-        $conditionString = '(' . rtrim($conditionString, '{op} ') . ')';
456
-        return ['conditionString' => $conditionString, 'conditionValues' => $conditionValues];
457
-    }
458
-
459
-    /**
460
-     * Wrap the given JSON selector.
461
-     *
462
-     * @param  string  $value
463
-     * @return string
464
-     */
465
-    protected function wrapJsonSelector(string $value): string
466
-    {
467
-        $removeLast = strpos($value, ')');
468
-        $value      = $removeLast === false ? $value : substr($value, 0, $removeLast);
469
-        $path       = explode('->', $value);
470
-        $field      = array_shift($path);
471
-        $result     = sprintf('%s->\'$.%s\'', $field, collect($path)->map(function ($part) {
472
-            return '"' . $part . '"';
473
-        })->implode('.'));
474
-
475
-        return $removeLast === false ? $result : $result . ')';
476
-    }
17
+	/**
18
+	 * @var Model
19
+	 */
20
+	public $model;
21
+
22
+	/**
23
+	 * Init new object.
24
+	 *
25
+	 * @param   Model$model
26
+	 * @return  void
27
+	 */
28
+	public function __construct(Model $model)
29
+	{
30
+		$this->model = $model;
31
+	}
32
+
33
+	/**
34
+	 * Fetch all records with relations from the storage.
35
+	 *
36
+	 * @param  array  $relations
37
+	 * @param  string $sortBy
38
+	 * @param  bool   $desc
39
+	 * @param  array  $columns
40
+	 * @return collection
41
+	 */
42
+	public function all(array $relations = [], string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): Collection
43
+	{
44
+		$sort = $desc ? 'desc' : 'asc';
45
+		return $this->model->with($relations)->orderBy($sortBy, $sort)->get($columns);
46
+	}
47
+
48
+	/**
49
+	 * Fetch all records with relations from storage in pages.
50
+	 *
51
+	 * @param  int    $perPage
52
+	 * @param  array  $relations
53
+	 * @param  string $sortBy
54
+	 * @param  bool   $desc
55
+	 * @param  array  $columns
56
+	 * @return LengthAwarePaginator
57
+	 */
58
+	public function paginate(int $perPage = 15, array $relations = [], string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): LengthAwarePaginator
59
+	{
60
+		$sort = $desc ? 'desc' : 'asc';
61
+		return $this->model->with($relations)->orderBy($sortBy, $sort)->paginate($perPage, $columns);
62
+	}
63
+
64
+	/**
65
+	 * Fetch all records with relations based on
66
+	 * the given condition from storage in pages.
67
+	 *
68
+	 * @param  array  $conditions array of conditions
69
+	 * @param  int    $perPage
70
+	 * @param  array  $relations
71
+	 * @param  string $sortBy
72
+	 * @param  bool   $desc
73
+	 * @param  array  $columns
74
+	 * @return LengthAwarePaginator
75
+	 */
76
+	public function paginateBy(array $conditions, int $perPage = 15, array $relations = [], string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): LengthAwarePaginator
77
+	{
78
+		$conditions = $this->constructConditions($conditions, $this->model);
79
+		$sort       = $desc ? 'desc' : 'asc';
80
+		return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->paginate($perPage, $columns);
81
+	}
82
+
83
+	/**
84
+	 * Count all records based on the given condition from storage.
85
+	 *
86
+	 * @param  array $conditions array of conditions
87
+	 * @return int
88
+	 */
89
+	public function count(array $conditions = []): int
90
+	{
91
+		if ($conditions) {
92
+			$conditions = $this->constructConditions($conditions, $this->model);
93
+			return $this->model->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->count();
94
+		}
95
+
96
+		return $this->model->count();
97
+	}
98
+
99
+	/**
100
+	 * Pluck column based on the given condition from storage.
101
+	 *
102
+	 * @param  array  $conditions array of conditions
103
+	 * @param  string $column
104
+	 * @return collection
105
+	 */
106
+	public function pluck(array $conditions, string $column): Collection
107
+	{
108
+		$conditions = $this->constructConditions($conditions, $this->model);
109
+		return $this->model->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->pluck($column);
110
+	}
111
+
112
+	/**
113
+	 * Save the given model to the storage.
114
+	 *
115
+	 * @param  array $data
116
+	 * @return Model
117
+	 */
118
+	public function save(array $data): Model
119
+	{
120
+		$model     = new Model();
121
+		$relations = [];
122
+
123
+		DB::transaction(function () use (&$model, &$relations, $data) {
124
+
125
+			$model = $this->prepareModel($data);
126
+			$model->save();
127
+
128
+			$relations = $this->prepareManyToManyRelations($data, $model);
129
+			$this->saveManyToManyRelation($model, $relations);
130
+		});
131
+
132
+		if (count($relations)) {
133
+			$model->load(...array_keys($relations));
134
+		}
135
+
136
+		return $model;
137
+	}
138
+
139
+	/**
140
+	 * Insert the given model/models to the storage.
141
+	 *
142
+	 * @param  array $data
143
+	 * @return bool
144
+	 */
145
+	public function insert(array $data): bool
146
+	{
147
+		return $this->model->insert($data);
148
+	}
149
+
150
+	/**
151
+	 * Delete record from the storage based on the given
152
+	 * condition.
153
+	 *
154
+	 * @param  string $value condition value
155
+	 * @param  string $attribute condition column name
156
+	 * @return bool
157
+	 */
158
+	public function delete(string $value, string $attribute = 'id'): bool
159
+	{
160
+		DB::transaction(function () use ($value, $attribute) {
161
+			$this->model->where($attribute, '=', $value)->lockForUpdate()->get()->each(function ($model) {
162
+				$model->delete();
163
+			});
164
+		});
165
+
166
+		return true;
167
+	}
168
+
169
+	/**
170
+	 * Fetch records from the storage based on the given
171
+	 * id.
172
+	 *
173
+	 * @param  int   $id
174
+	 * @param  array $relations
175
+	 * @param  array $columns
176
+	 * @return Model
177
+	 */
178
+	public function find(int $id, array $relations = [], array $columns = ['*']): Model
179
+	{
180
+		return $this->model->with($relations)->find($id, $columns);
181
+	}
182
+
183
+	/**
184
+	 * Fetch records from the storage based on the given
185
+	 * condition.
186
+	 *
187
+	 * @param  array   $conditions array of conditions
188
+	 * @param  array   $relations
189
+	 * @param  string  $sortBy
190
+	 * @param  bool    $desc
191
+	 * @param  array   $columns
192
+	 * @return collection
193
+	 */
194
+	public function findBy(array $conditions, array $relations = [], string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): Collection
195
+	{
196
+		$conditions = $this->constructConditions($conditions, $this->model);
197
+		$sort       = $desc ? 'desc' : 'asc';
198
+		return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->get($columns);
199
+	}
200
+
201
+	/**
202
+	 * Fetch the first record from the storage based on the given
203
+	 * condition.
204
+	 *
205
+	 * @param  array $conditions array of conditions
206
+	 * @param  array $relations
207
+	 * @param  array $columns
208
+	 * @return Model
209
+	 */
210
+	public function first(array $conditions, array $relations = [], array $columns = ['*']): Model
211
+	{
212
+		$conditions = $this->constructConditions($conditions, $this->model);
213
+		return $this->model->with($relations)->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->first($columns);
214
+	}
215
+
216
+	/**
217
+	 * Return the deleted models in pages based on the given conditions.
218
+	 *
219
+	 * @param  array  $conditions array of conditions
220
+	 * @param  int    $perPage
221
+	 * @param  string $sortBy
222
+	 * @param  bool   $desc
223
+	 * @param  array  $columns
224
+	 * @return LengthAwarePaginator
225
+	 */
226
+	public function deleted(array $conditions, int $perPage = 15, string $sortBy = 'created_at', bool $desc = true, array $columns = ['*']): LengthAwarePaginator
227
+	{
228
+		unset($conditions['page'], $conditions['perPage'], $conditions['sortBy'], $conditions['sort']);
229
+		$conditions = $this->constructConditions($conditions, $this->model);
230
+		$sort       = $desc ? 'desc' : 'asc';
231
+		$model      = $this->model->onlyTrashed();
232
+
233
+		if (count($conditions['conditionValues'])) {
234
+			$model->whereRaw($conditions['conditionString'], $conditions['conditionValues']);
235
+		}
236
+
237
+		return $model->orderBy($sortBy, $sort)->paginate($perPage, $columns);
238
+	}
239
+
240
+	/**
241
+	 * Restore the deleted model.
242
+	 *
243
+	 * @param  int $id
244
+	 * @return bool
245
+	 */
246
+	public function restore(int $id): bool
247
+	{
248
+		$model = $this->model->onlyTrashed()->find($id);
249
+
250
+		if (!$model) {
251
+			Errors::notFound(class_basename($this->model) . ' with id : ' . $id);
252
+		}
253
+
254
+		$model->restore();
255
+
256
+		return true;
257
+	}
258
+
259
+	/**
260
+	 * Fill the model with the given data.
261
+	 *
262
+	 * @param   array  $data
263
+	 * @return  Model
264
+	 */
265
+	protected function prepareModel(array $data): Model
266
+	{
267
+		$modelClass = $this->model;
268
+		$model = Arr::has($data, 'id') ? $modelClass->lockForUpdate()->find($data['id']) : new $modelClass;
269
+
270
+		if (!$model) {
271
+			Errors::notFound(class_basename($modelClass) . ' with id : ' . $data['id']);
272
+		}
273
+
274
+		foreach ($data as $key => $value) {
275
+			if (array_search($key, $model->getFillable(), true) !== false) {
276
+				$model->$key = $value;
277
+			}
278
+		}
279
+
280
+		return $model;
281
+	}
282
+
283
+	/**
284
+	 * Prepare many to many relation if found.
285
+	 *
286
+	 * @param   array $data
287
+	 * @param   Model $model
288
+	 *
289
+	 * @return  array
290
+	 */
291
+	protected function prepareManyToManyRelations(array $data, Model $model): array
292
+	{
293
+		$relations = [];
294
+		foreach ($data as $key => $relatedModels) {
295
+			$relation = Str::camel($key);
296
+			if (method_exists($model, $relation) &&
297
+				Core::$relation() &&
298
+				in_array(class_basename($model->$key()), ['MorphToMany', 'BelongsToMany'])
299
+			) {
300
+				if (!$relatedModels || !count($relatedModels)) {
301
+					/**
302
+					 * Mark the relation to be deleted.
303
+					 */
304
+					$relations[$relation] = 'delete';
305
+				}
306
+
307
+				foreach ($relatedModels as $relatedModel) {
308
+					$relationBaseModel = Core::$relation()->model;
309
+					$relations[$relation][] = $this->prepareRelatedModel($relatedModel, $relationBaseModel);
310
+				}
311
+			}
312
+		}
313
+
314
+		return $relations;
315
+	}
316
+
317
+	/**
318
+	 * Prepare related models with extra values for pivot if found.
319
+	 *
320
+	 * @param   mixed  $relatedModelData
321
+	 * @param   Model  $relationBaseModel
322
+	 *
323
+	 * @return  Model
324
+	 */
325
+	protected function prepareRelatedModel(mixed $relatedModelData, Model $relationBaseModel): Model
326
+	{
327
+		if (!is_array($relatedModelData)) { // if the relation is integer id
328
+			$relatedModel = $relationBaseModel->lockForUpdate()->find($relatedModelData);
329
+		} else {
330
+			$relatedModel = Arr::has($relatedModelData, 'id') ? $relationBaseModel->lockForUpdate()->find($relatedModelData['id']) : new $relationBaseModel;
331
+		}
332
+
333
+		if (!$relatedModel) {
334
+			Errors::notFound(class_basename($relationBaseModel) . ' with id : ' . $relatedModelData['id']);
335
+		}
336
+
337
+		if (is_array($relatedModelData)) {
338
+			foreach ($relatedModelData as $attr => $val) {
339
+				if (array_search($attr, $relatedModel->getFillable(), true) === false &&
340
+					gettype($val) !== 'object' &&
341
+					gettype($val) !== 'array' &&
342
+					$attr !== 'id'
343
+				) {
344
+					$extra[$attr] = $val;
345
+				}
346
+			}
347
+		}
348
+
349
+		if (isset($extra)) {
350
+			$relatedModel->extra = $extra;
351
+		}
352
+
353
+		return $relatedModel;
354
+	}
355
+
356
+	/**
357
+	 * Save the given model many to many relations.
358
+	 *
359
+	 * @param   Model $model
360
+	 * @param   array $relations
361
+	 *
362
+	 * @return  void
363
+	 */
364
+	protected function saveManyToManyRelation(Model $model, array $relations)
365
+	{
366
+		foreach ($relations as $key => $value) {
367
+			/**
368
+			 * If the relation is marked for delete then delete it.
369
+			 */
370
+			if ($value == 'delete' && $model->$key()->count()) {
371
+				$model->$key()->detach();
372
+			} else {
373
+				$ids = [];
374
+				foreach ($value as $val) {
375
+					$extra = $val->extra;
376
+					$ids[$val->id] = $extra ?? [];
377
+					unset($val->extra);
378
+				}
379
+
380
+				$model->$key()->detach();
381
+				$model->$key()->attach($ids);
382
+			}
383
+		}
384
+	}
385
+
386
+	/**
387
+	 * Build the conditions recursively for the retrieving methods.
388
+	 *
389
+	 * @param  array $conditions
390
+	 * @param  Model $model
391
+	 * @return array
392
+	 */
393
+	protected function constructConditions(array $conditions, Model $model): array
394
+	{
395
+		$conditionString = '';
396
+		$conditionValues = [];
397
+		foreach ($conditions as $key => $value) {
398
+			if (Str::contains($key, '->')) {
399
+				$key = $this->wrapJsonSelector($key);
400
+			}
401
+
402
+			if ($key == 'and') {
403
+				$conditions       = $this->constructConditions($value, $model);
404
+				$conditionString .= str_replace('{op}', 'and', $conditions['conditionString']) . ' {op} ';
405
+				$conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
406
+			} elseif ($key == 'or') {
407
+				$conditions       = $this->constructConditions($value, $model);
408
+				$conditionString .= str_replace('{op}', 'or', $conditions['conditionString']) . ' {op} ';
409
+				$conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
410
+			} else {
411
+				if (is_array($value)) {
412
+					$operator = $value['op'];
413
+					if (strtolower($operator) == 'between') {
414
+						$value1 = $value['val1'];
415
+						$value2 = $value['val2'];
416
+					} else {
417
+						$value = Arr::get($value, 'val', '');
418
+					}
419
+				} else {
420
+					$operator = '=';
421
+				}
422
+
423
+				if (strtolower($operator) == 'between') {
424
+					$conditionString  .= $key . ' >= ? and ';
425
+					$conditionValues[] = $value1;
426
+
427
+					$conditionString  .= $key . ' <= ? {op} ';
428
+					$conditionValues[] = $value2;
429
+				} elseif (strtolower($operator) == 'in') {
430
+					$conditionValues  = array_merge($conditionValues, $value);
431
+					$inBindingsString = rtrim(str_repeat('?,', count($value)), ',');
432
+					$conditionString .= $key . ' in (' . rtrim($inBindingsString, ',') . ') {op} ';
433
+				} elseif (strtolower($operator) == 'null') {
434
+					$conditionString .= $key . ' is null {op} ';
435
+				} elseif (strtolower($operator) == 'not null') {
436
+					$conditionString .= $key . ' is not null {op} ';
437
+				} elseif (strtolower($operator) == 'has') {
438
+					$sql              = $model->withTrashed()->withoutGlobalScopes()->has($key)->toSql();
439
+					$bindings         = $model->withTrashed()->withoutGlobalScopes()->has($key)->getBindings();
440
+					if ($value) {
441
+						$conditions       = $this->constructConditions($value, $model->$key()->getRelated());
442
+						$conditionString .= substr(substr($sql, strpos($sql, 'exists')), 0, -1) . ' and ' . $conditions['conditionString'] . ') {op} ';
443
+						$conditionValues  = array_merge($conditionValues, $bindings);
444
+						$conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
445
+					} else {
446
+						$conditionString .= substr(substr($sql, strpos($sql, 'exists')), 0, -1) . ') {op} ';
447
+						$conditionValues  = array_merge($conditionValues, $bindings);
448
+					}
449
+				} else {
450
+					$conditionString  .= $key . ' ' . $operator . ' ? {op} ';
451
+					$conditionValues[] = $value;
452
+				}
453
+			}
454
+		}
455
+		$conditionString = '(' . rtrim($conditionString, '{op} ') . ')';
456
+		return ['conditionString' => $conditionString, 'conditionValues' => $conditionValues];
457
+	}
458
+
459
+	/**
460
+	 * Wrap the given JSON selector.
461
+	 *
462
+	 * @param  string  $value
463
+	 * @return string
464
+	 */
465
+	protected function wrapJsonSelector(string $value): string
466
+	{
467
+		$removeLast = strpos($value, ')');
468
+		$value      = $removeLast === false ? $value : substr($value, 0, $removeLast);
469
+		$path       = explode('->', $value);
470
+		$field      = array_shift($path);
471
+		$result     = sprintf('%s->\'$.%s\'', $field, collect($path)->map(function ($part) {
472
+			return '"' . $part . '"';
473
+		})->implode('.'));
474
+
475
+		return $removeLast === false ? $result : $result . ')';
476
+	}
477 477
 }
Please login to merge, or discard this patch.
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -120,7 +120,7 @@  discard block
 block discarded – undo
120 120
         $model     = new Model();
121 121
         $relations = [];
122 122
 
123
-        DB::transaction(function () use (&$model, &$relations, $data) {
123
+        DB::transaction(function() use (&$model, &$relations, $data) {
124 124
 
125 125
             $model = $this->prepareModel($data);
126 126
             $model->save();
@@ -157,8 +157,8 @@  discard block
 block discarded – undo
157 157
      */
158 158
     public function delete(string $value, string $attribute = 'id'): bool
159 159
     {
160
-        DB::transaction(function () use ($value, $attribute) {
161
-            $this->model->where($attribute, '=', $value)->lockForUpdate()->get()->each(function ($model) {
160
+        DB::transaction(function() use ($value, $attribute) {
161
+            $this->model->where($attribute, '=', $value)->lockForUpdate()->get()->each(function($model) {
162 162
                 $model->delete();
163 163
             });
164 164
         });
@@ -247,8 +247,8 @@  discard block
 block discarded – undo
247 247
     {
248 248
         $model = $this->model->onlyTrashed()->find($id);
249 249
 
250
-        if (!$model) {
251
-            Errors::notFound(class_basename($this->model) . ' with id : ' . $id);
250
+        if ( ! $model) {
251
+            Errors::notFound(class_basename($this->model).' with id : '.$id);
252 252
         }
253 253
 
254 254
         $model->restore();
@@ -267,8 +267,8 @@  discard block
 block discarded – undo
267 267
         $modelClass = $this->model;
268 268
         $model = Arr::has($data, 'id') ? $modelClass->lockForUpdate()->find($data['id']) : new $modelClass;
269 269
 
270
-        if (!$model) {
271
-            Errors::notFound(class_basename($modelClass) . ' with id : ' . $data['id']);
270
+        if ( ! $model) {
271
+            Errors::notFound(class_basename($modelClass).' with id : '.$data['id']);
272 272
         }
273 273
 
274 274
         foreach ($data as $key => $value) {
@@ -297,7 +297,7 @@  discard block
 block discarded – undo
297 297
                 Core::$relation() &&
298 298
                 in_array(class_basename($model->$key()), ['MorphToMany', 'BelongsToMany'])
299 299
             ) {
300
-                if (!$relatedModels || !count($relatedModels)) {
300
+                if ( ! $relatedModels || ! count($relatedModels)) {
301 301
                     /**
302 302
                      * Mark the relation to be deleted.
303 303
                      */
@@ -324,14 +324,14 @@  discard block
 block discarded – undo
324 324
      */
325 325
     protected function prepareRelatedModel(mixed $relatedModelData, Model $relationBaseModel): Model
326 326
     {
327
-        if (!is_array($relatedModelData)) { // if the relation is integer id
327
+        if ( ! is_array($relatedModelData)) { // if the relation is integer id
328 328
             $relatedModel = $relationBaseModel->lockForUpdate()->find($relatedModelData);
329 329
         } else {
330 330
             $relatedModel = Arr::has($relatedModelData, 'id') ? $relationBaseModel->lockForUpdate()->find($relatedModelData['id']) : new $relationBaseModel;
331 331
         }
332 332
 
333
-        if (!$relatedModel) {
334
-            Errors::notFound(class_basename($relationBaseModel) . ' with id : ' . $relatedModelData['id']);
333
+        if ( ! $relatedModel) {
334
+            Errors::notFound(class_basename($relationBaseModel).' with id : '.$relatedModelData['id']);
335 335
         }
336 336
 
337 337
         if (is_array($relatedModelData)) {
@@ -401,11 +401,11 @@  discard block
 block discarded – undo
401 401
 
402 402
             if ($key == 'and') {
403 403
                 $conditions       = $this->constructConditions($value, $model);
404
-                $conditionString .= str_replace('{op}', 'and', $conditions['conditionString']) . ' {op} ';
404
+                $conditionString .= str_replace('{op}', 'and', $conditions['conditionString']).' {op} ';
405 405
                 $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
406 406
             } elseif ($key == 'or') {
407 407
                 $conditions       = $this->constructConditions($value, $model);
408
-                $conditionString .= str_replace('{op}', 'or', $conditions['conditionString']) . ' {op} ';
408
+                $conditionString .= str_replace('{op}', 'or', $conditions['conditionString']).' {op} ';
409 409
                 $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
410 410
             } else {
411 411
                 if (is_array($value)) {
@@ -421,38 +421,38 @@  discard block
 block discarded – undo
421 421
                 }
422 422
 
423 423
                 if (strtolower($operator) == 'between') {
424
-                    $conditionString  .= $key . ' >= ? and ';
424
+                    $conditionString  .= $key.' >= ? and ';
425 425
                     $conditionValues[] = $value1;
426 426
 
427
-                    $conditionString  .= $key . ' <= ? {op} ';
427
+                    $conditionString  .= $key.' <= ? {op} ';
428 428
                     $conditionValues[] = $value2;
429 429
                 } elseif (strtolower($operator) == 'in') {
430 430
                     $conditionValues  = array_merge($conditionValues, $value);
431 431
                     $inBindingsString = rtrim(str_repeat('?,', count($value)), ',');
432
-                    $conditionString .= $key . ' in (' . rtrim($inBindingsString, ',') . ') {op} ';
432
+                    $conditionString .= $key.' in ('.rtrim($inBindingsString, ',').') {op} ';
433 433
                 } elseif (strtolower($operator) == 'null') {
434
-                    $conditionString .= $key . ' is null {op} ';
434
+                    $conditionString .= $key.' is null {op} ';
435 435
                 } elseif (strtolower($operator) == 'not null') {
436
-                    $conditionString .= $key . ' is not null {op} ';
436
+                    $conditionString .= $key.' is not null {op} ';
437 437
                 } elseif (strtolower($operator) == 'has') {
438 438
                     $sql              = $model->withTrashed()->withoutGlobalScopes()->has($key)->toSql();
439 439
                     $bindings         = $model->withTrashed()->withoutGlobalScopes()->has($key)->getBindings();
440 440
                     if ($value) {
441 441
                         $conditions       = $this->constructConditions($value, $model->$key()->getRelated());
442
-                        $conditionString .= substr(substr($sql, strpos($sql, 'exists')), 0, -1) . ' and ' . $conditions['conditionString'] . ') {op} ';
442
+                        $conditionString .= substr(substr($sql, strpos($sql, 'exists')), 0, -1).' and '.$conditions['conditionString'].') {op} ';
443 443
                         $conditionValues  = array_merge($conditionValues, $bindings);
444 444
                         $conditionValues  = array_merge($conditionValues, $conditions['conditionValues']);
445 445
                     } else {
446
-                        $conditionString .= substr(substr($sql, strpos($sql, 'exists')), 0, -1) . ') {op} ';
446
+                        $conditionString .= substr(substr($sql, strpos($sql, 'exists')), 0, -1).') {op} ';
447 447
                         $conditionValues  = array_merge($conditionValues, $bindings);
448 448
                     }
449 449
                 } else {
450
-                    $conditionString  .= $key . ' ' . $operator . ' ? {op} ';
450
+                    $conditionString  .= $key.' '.$operator.' ? {op} ';
451 451
                     $conditionValues[] = $value;
452 452
                 }
453 453
             }
454 454
         }
455
-        $conditionString = '(' . rtrim($conditionString, '{op} ') . ')';
455
+        $conditionString = '('.rtrim($conditionString, '{op} ').')';
456 456
         return ['conditionString' => $conditionString, 'conditionValues' => $conditionValues];
457 457
     }
458 458
 
@@ -468,10 +468,10 @@  discard block
 block discarded – undo
468 468
         $value      = $removeLast === false ? $value : substr($value, 0, $removeLast);
469 469
         $path       = explode('->', $value);
470 470
         $field      = array_shift($path);
471
-        $result     = sprintf('%s->\'$.%s\'', $field, collect($path)->map(function ($part) {
472
-            return '"' . $part . '"';
471
+        $result     = sprintf('%s->\'$.%s\'', $field, collect($path)->map(function($part) {
472
+            return '"'.$part.'"';
473 473
         })->implode('.'));
474 474
 
475
-        return $removeLast === false ? $result : $result . ')';
475
+        return $removeLast === false ? $result : $result.')';
476 476
     }
477 477
 }
Please login to merge, or discard this patch.