Completed
Push — master ( 262a61...e9fbf8 )
by Sherif
02:59
created

AbstractRepository::delete()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
c 2
b 1
f 1
dl 0
loc 25
rs 8.439
cc 5
eloc 13
nc 2
nop 3
1
<?php namespace App\Modules\V1\Core\AbstractRepositories;
2
3
use App\Modules\V1\Core\Interfaces\RepositoryInterface;
4
5
abstract class AbstractRepository implements RepositoryInterface
6
{
7
    /**
8
     * The model implementation.
9
     * 
10
     * @var model
11
     */
12
    public $model;
13
    
14
    /**
15
     * The config implementation.
16
     * 
17
     * @var config
18
     */
19
    protected $config;
20
    
21
    /**
22
     * Create new AbstractRepository instance.
23
     */
24
    public function __construct()
25
    {   
26
        $this->config = \CoreConfig::getConfig();
27
        $this->model  = \App::make($this->getModel());
28
    }
29
30
    /**
31
     * Fetch all records with relations from the storage.
32
     *
33
     * @param  array   $relations
34
     * @param  string  $sortBy
35
     * @param  boolean $desc
36
     * @param  array   $columns
37
     * @return collection
38
     */
39
    public function all($relations = [], $sortBy = 'created_at', $desc = 1, $columns = array('*'))
40
    {
41
        $sort = $desc ? 'desc' : 'asc';
42
        return call_user_func_array("{$this->getModel()}::with", array($relations))->orderBy($sortBy, $sort)->get($columns);
43
    }
44
45
    /**
46
     * Fetch all records with relations from storage in pages 
47
     * that matche the given query.
48
     * 
49
     * @param  string  $query
50
     * @param  integer $perPage
51
     * @param  array   $relations
52
     * @param  string  $sortBy
53
     * @param  boolean $desc
54
     * @param  array   $columns
55
     * @return collection
56
     */
57
    public function search($query, $perPage = 15, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = array('*'))
58
    {
59
        $model            = call_user_func_array("{$this->getModel()}::with", array($relations));
60
        $conditionColumns = $this->model->getFillable();
61
        $sort             = $desc ? 'desc' : 'asc';
62
63
        /**
64
         * Construct the select conditions for the model.
65
         */
66
        $model->where(function ($q) use ($query, $conditionColumns, $relations){
67
68
            /**
69
             * Use the first element in the model columns to construct the first condition.
70
             */
71
            $q->where(\DB::raw('LOWER(CAST(' .array_shift($conditionColumns). ' AS TEXT))'), 'LIKE', '%' . strtolower($query) . '%');
72
73
            /**
74
             * Loop through the rest of the columns to construct or where conditions.
75
             */
76
            foreach ($conditionColumns as $column) 
77
            {
78
                $q->orWhere(\DB::raw('LOWER(CAST(' . $column . ' AS TEXT))'), 'LIKE', '%' . strtolower($query) . '%');
79
            }
80
81
            /**
82
             * Loop through the model relations.
83
             */
84
            foreach ($relations as $relation) 
85
            {
86
                /**
87
                 * Remove the sub relation if exists.
88
                 */
89
                $relation = explode('.', $relation)[0];
90
91
                /**
92
                 * Try to fetch the relation repository from the core.
93
                 */
94
                if (\Core::$relation()) 
95
                {
96
                    /**
97
                     * Construct the relation condition.
98
                     */
99
                    $q->orWhereHas($relation, function ($subModel) use ($query, $relation){
100
101
                        $subModel->where(function ($q) use ($query, $relation){
102
103
                            /**
104
                             * Get columns of the relation.
105
                             */
106
                            $subConditionColumns = \Core::$relation()->model->getFillable();
107
108
                            /**
109
                             * Use the first element in the relation model columns to construct the first condition.
110
                             */
111
                            $q->where(\DB::raw('LOWER(CAST(' . array_shift($subConditionColumns) . ' AS TEXT))'), 'LIKE', '%' . strtolower($query) . '%');
112
113
                            /**
114
                             * Loop through the rest of the columns to construct or where conditions.
115
                             */
116
                            foreach ($subConditionColumns as $subConditionColumn)
117
                            {
118
                                $q->orWhere(\DB::raw('LOWER(CAST(' . $subConditionColumn . ' AS TEXT))'), 'LIKE', '%' . strtolower($query) . '%');
119
                            } 
120
                        });
121
122
                    });
123
                }
124
            }
125
        });
126
        
127
        return $model->orderBy($sortBy, $sort)->paginate($perPage, $columns);
128
    }
129
    
130
    /**
131
     * Fetch all records with relations from storage in pages.
132
     * 
133
     * @param  integer $perPage
134
     * @param  array   $relations
135
     * @param  string  $sortBy
136
     * @param  boolean $desc
137
     * @param  array   $columns
138
     * @return collection
139
     */
140
    public function paginate($perPage = 15, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = array('*'))
141
    {
142
        $sort = $desc ? 'desc' : 'asc';
143
        return call_user_func_array("{$this->getModel()}::with", array($relations))->orderBy($sortBy, $sort)->paginate($perPage, $columns);
144
    }
145
146
    /**
147
     * Fetch all records with relations based on
148
     * the given condition from storage in pages.
149
     * 
150
     * @param  array   $conditions array of conditions
151
     * @param  integer $perPage
152
     * @param  array   $relations
153
     * @param  string  $sortBy
154
     * @param  boolean $desc
155
     * @param  array   $columns
156
     * @return collection
157
     */
158
    public function paginateBy($conditions, $perPage = 15, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = array('*'))
159
    {
160
        unset($conditions['page']);
161
        $conditions = $this->constructConditions($conditions);
162
        $sort       = $desc ? 'desc' : 'asc';
163
        return call_user_func_array("{$this->getModel()}::with", array($relations))->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->paginate($perPage, $columns);
164
    }
165
    
166
    /**
167
     * Save the given model to the storage.
168
     * 
169
     * @param  array   $data
170
     * @param  boolean $saveLog
171
     * @return object
172
     */
173
    public function save(array $data, $saveLog = true)
174
    {
175
        $model      = false;
176
        $modelClass = $this->model;
177
        $relations  = [];
178
        $with       = [];
179
180
        \DB::transaction(function () use (&$model, &$relations, &$with, $data, $saveLog, $modelClass) {
181
            /**
182
             * If the id is present in the data then select the model for updating,
183
             * else create new model.
184
             * @var array
185
             */
186
            $model = array_key_exists('id', $data) ? $modelClass->lockForUpdate()->find($data['id']) : new $modelClass;
187
            if ( ! $model) 
188
            {
189
                \ErrorHandler::notFound(class_basename($modelClass) . ' with id : ' . $data['id']);
190
            }
191
192
            /**
193
             * Construct the model object with the given data,
194
             * and if there is a relation add it to relations array,
195
             * then save the model.
196
             */
197
            foreach ($data as $key => $value) 
198
            {
199
                /**
200
                 * If the attribute is a relation.
201
                 */
202
                $relation = camel_case($key);
203
                if (method_exists($model, $relation))
204
                {
205
                    /**
206
                     * Add the relation to the with array to eager load the created model with the relations.
207
                     */
208
                    $with[] = $relation;
209
210
                    /**
211
                     * Check if the relation is a collection.
212
                     */
213
                    if (class_basename($model->$relation) == 'Collection') 
214
                    {   
215
                        /**
216
                         * If the relation has no value then marke the relation data 
217
                         * related to the model to be deleted.
218
                         */
219
                        if ( ! $value || ! count($value)) 
220
                        {
221
                            $relations[$relation] = 'delete';
222
                        }   
223
                    }
224
                    if (is_array($value)) 
225
                    {
226
                        /**
227
                         * Loop through the relation data.
228
                         */
229
                        foreach ($value as $attr => $val) 
230
                        {
231
                            /**
232
                             * Get the relation model.
233
                             */
234
                            $relationBaseModel = \Core::$relation()->model;
235
236
                            /**
237
                             * Check if the relation is a collection.
238
                             */
239
                            if (class_basename($model->$relation) == 'Collection')
240
                            {
241
                                /**
242
                                 * If the id is present in the data then select the relation model for updating,
243
                                 * else create new model.
244
                                 */
245
                                $relationModel = array_key_exists('id', $val) ? $relationBaseModel->lockForUpdate()->find($val['id']) : new $relationBaseModel;
246
247
                                /**
248
                                 * If model doesn't exists.
249
                                 */
250
                                if ( ! $relationModel) 
251
                                {
252
                                    \ErrorHandler::notFound(class_basename($relationBaseModel) . ' with id : ' . $val['id']);
253
                                }
254
255
                                /**
256
                                 * Loop through the relation attributes.
257
                                 */
258
                                foreach ($val as $attr => $val) 
259
                                {
260
                                    /**
261
                                     * Prevent the sub relations or attributes not in the fillable.
262
                                     */
263
                                    if (gettype($val) !== 'object' && gettype($val) !== 'array' &&  array_search($attr, $relationModel->getFillable(), true) !== false)
264
                                    {
265
                                        $relationModel->$attr = $val;
266
                                    }
267
                                }
268
                                $relations[$relation][] = $relationModel;
269
                            }
270
                            /**
271
                             * If not collection.
272
                             */
273
                            else
274
                            {
275
                                /**
276
                                 * Prevent the sub relations.
277
                                 */
278
                                if (gettype($val) !== 'object' && gettype($val) !== 'array') 
279
                                {
280
                                    /**
281
                                     * If the id is present in the data then select the relation model for updating,
282
                                     * else create new model.
283
                                     */
284
                                    $relationModel = array_key_exists('id', $value) ? $relationBaseModel->lockForUpdate()->find($value['id']) : new $relationBaseModel;
285
286
                                    /**
287
                                     * If model doesn't exists.
288
                                     */
289
                                    if ( ! $relationModel) 
290
                                    {
291
                                        \ErrorHandler::notFound(class_basename($relationBaseModel) . ' with id : ' . $value['id']);
292
                                    }
293
294
                                    /**
295
                                     * Prevent attributes not in the fillable.
296
                                     */
297
                                    if (array_search($attr, $relationModel->getFillable(), true) !== false) 
298
                                    {
299
                                        $relationModel->$attr = $val;
300
                                        $relations[$relation] = $relationModel;
301
                                    }
302
                                }
303
                            }
304
                        }
305
                    }
306
                }
307
                /**
308
                 * If the attribute isn't a relation and prevent attributes not in the fillable.
309
                 */
310
                else if (array_search($key, $model->getFillable(), true) !== false)
311
                {
312
                    $model->$key = $value;   
313
                }
314
            }
315
            /**
316
             * Save the model.
317
             */
318
            $model->save();
319
            
320
            /**
321
             * Loop through the relations array.
322
             */
323
            foreach ($relations as $key => $value) 
324
            {
325
                /**
326
                 * If the relation is marked for delete then delete it.
327
                 */
328
                if ($value == 'delete' && $model->$key()->count())
329
                {
330
                    $model->$key()->delete();
331
                }
332
                /**
333
                 * If the relation is an array.
334
                 */
335
                else if (gettype($value) == 'array') 
336
                {
337
                    $ids = [];
338
                    /**
339
                     * Loop through the relations.
340
                     */
341
                    foreach ($value as $val) 
342
                    {
343
                        switch (class_basename($model->$key())) 
344
                        {
345
                            /**
346
                             * If the relation is one to many then update it's foreign key with
347
                             * the model id and save it then add its id to ids array to delete all 
348
                             * relations who's id isn't in the ids array.
349
                             */
350
                            case 'HasMany':
351
                                $foreignKeyName       = explode('.', $model->$key()->getForeignKey())[1];
352
                                $val->$foreignKeyName = $model->id;
353
                                $val->save();
354
                                $ids[] = $val->id;
355
                                break;
356
357
                            /**
358
                             * If the relation is many to many then add it's id to the ids array to
359
                             * attache these ids to the model.
360
                             */
361
                            case 'BelongsToMany':
362
                                $val->save();
363
                                $ids[] = $val->id;
364
                                break;
365
                        }
366
                    }
367
                    switch (class_basename($model->$key())) 
368
                    {
369
                        /**
370
                         * If the relation is one to many then delete all 
371
                         * relations who's id isn't in the ids array.
372
                         */
373
                        case 'HasMany':
374
                            $model->$key()->whereNotIn('id', $ids)->delete();
375
                            break;
376
377
                        /**
378
                         * If the relation is many to many then 
379
                         * detach the previous data and attach 
380
                         * the ids array to the model.
381
                         */
382
                        case 'BelongsToMany':
383
                            $model->$key()->detach();
384
                            $model->$key()->attach($ids);
385
                            break;
386
                    }
387
                }
388
                /**
389
                 * If the relation isn't array.
390
                 */
391
                else
392
                {
393
                    switch (class_basename($model->$key())) 
394
                    {
395
                        /**
396
                         * If the relation is one to many or one to one.
397
                         */
398
                        case 'BelongsTo':
399
                            $value->save();
400
                            $model->$key()->associate($value);
401
                            $model->save();
402
                            break;
403
                    }
404
                }
405
            }
406
407
            $saveLog ? \Logging::saveLog(array_key_exists('id', $data) ? 'update' : 'create', class_basename($modelClass), $this->getModel(), $model->id, $model) : false;
408
        });
409
    
410
        /**
411
         * return the saved mdel with the given relations.
412
         */
413
        return $this->find($model->id, $with);
414
    }
415
    
416
    /**
417
     * Delete record from the storage based on the given
418
     * condition.
419
     * 
420
     * @param  var $value condition value
421
     * @param  string $attribute condition column name
422
     * @return void
423
     */
424
    public function delete($value, $attribute = 'id', $saveLog = true)
425
    {
426
        if ($attribute == 'id') 
427
        {
428
            \DB::transaction(function () use ($value, $attribute, &$result, $saveLog) {
429
                $model = $this->model->lockForUpdate()->find($value);
430
                if ( ! $model) 
431
                {
432
                    \ErrorHandler::notFound(class_basename($this->model) . ' with id : ' . $value);
433
                }
434
                
435
                $model->delete();
436
                $saveLog ? \Logging::saveLog('delete', class_basename($this->model), $this->getModel(), $value, $model) : false;
437
            });
438
        }
439
        else
440
        {
441
            \DB::transaction(function () use ($value, $attribute, &$result, $saveLog) {
442
                call_user_func_array("{$this->getModel()}::where", array($attribute, '=', $value))->lockForUpdate()->get()->each(function ($model){
443
                    $model->delete();
444
                    $saveLog ? \Logging::saveLog('delete', class_basename($this->model), $this->getModel(), $model->id, $model) : false;
0 ignored issues
show
Bug introduced by
The variable $saveLog does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
445
                });
446
            });   
447
        }
448
    }
449
    
450
    /**
451
     * Fetch records from the storage based on the given
452
     * id.
453
     * 
454
     * @param  integer $id
455
     * @param  array   $relations
456
     * @param  array   $columns
457
     * @return object
458
     */
459
    public function find($id, $relations = [], $columns = array('*'))
460
    {
461
        return call_user_func_array("{$this->getModel()}::with", array($relations))->find($id, $columns);
462
    }
463
    
464
    /**
465
     * Fetch records from the storage based on the given
466
     * condition.
467
     * 
468
     * @param  array   $conditions array of conditions
469
     * @param  array   $relations
470
     * @param  string  $sortBy
471
     * @param  boolean $desc
472
     * @param  array   $columns
473
     * @return collection
474
     */
475
    public function findBy($conditions, $relations = [], $sortBy = 'created_at', $desc = 1, $columns = array('*'))
476
    {
477
        $conditions = $this->constructConditions($conditions);
478
        $sort       = $desc ? 'desc' : 'asc';
479
        return call_user_func_array("{$this->getModel()}::with",  array($relations))->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->orderBy($sortBy, $sort)->get($columns);
480
    }
481
482
    /**
483
     * Fetch the first record from the storage based on the given
484
     * condition.
485
     *
486
     * @param  array   $conditions array of conditions
487
     * @param  array   $relations
488
     * @param  array   $columns
489
     * @return object
490
     */
491
    public function first($conditions, $relations = [], $columns = array('*'))
492
    {
493
        $conditions = $this->constructConditions($conditions);
494
        return call_user_func_array("{$this->getModel()}::with", array($relations))->whereRaw($conditions['conditionString'], $conditions['conditionValues'])->first($columns);  
495
    }
496
497
    /**
498
     * Build the conditions recursively for the retrieving methods.
499
     * @param  array $conditions
500
     * @return array
501
     */
502
    protected function constructConditions($conditions)
503
    {   
504
        $conditionString = '';
505
        $conditionValues = [];
506
        foreach ($conditions as $key => $value) 
507
        {
508
            if ($key == 'and') 
509
            {
510
                $conditionString  .= str_replace('{op}', 'and', $this->constructConditions($value)['conditionString']) . ' {op} ';
511
                $conditionValues   = array_merge($conditionValues, $this->constructConditions($value)['conditionValues']);
512
            }
513
            else if ($key == 'or')
514
            {
515
                $conditionString  .= str_replace('{op}', 'or', $this->constructConditions($value)['conditionString']) . ' {op} ';
516
                $conditionValues   = array_merge($conditionValues, $this->constructConditions($value)['conditionValues']);
517
            }
518
            else
519
            {
520
                $conditionString  .= $key . '=? {op} ';
521
                $conditionValues[] = $value;
522
            }
523
        }
524
        $conditionString = '(' . rtrim($conditionString, '{op} ') . ')';
525
        return ['conditionString' => $conditionString, 'conditionValues' => $conditionValues];
526
    }
527
528
    /**
529
     * Abstract method that return the necessary 
530
     * information (full model namespace)
531
     * needed to preform the previous actions.
532
     * 
533
     * @return string
534
     */
535
    abstract protected function getModel();
536
}