Passed
Push — master ( d22725...07a307 )
by Henri
01:26
created

Datamanager::mountSave()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 7
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 15
rs 8.8333
1
<?php
2
3
namespace HnrAzevedo\Datamanager;
4
5
use Exception;
6
7
abstract class Datamanager
8
{
9
    use CrudTrait;
10
11
    private ?string $table = null;
12
    private ?string $primary = null;
13
    private array $result = [];
14
    protected array $data = [];
15
    
16
    private bool $full = false;
17
    private ?string $clause = null;
18
19
20
    private array $where = [''=> ["1",'=',"1"] ];
21
    private ?string $order = null;
22
    private ?string $limit = null;
23
    private ?int $offset = null;
24
    private array $excepts = [];
25
    private int $count = 0;
26
    private array $select = [];
27
    private ?string $query = null;
28
29
30
    protected function create(string $table, string $primary): Datamanager
31
    {
32
        $this->table = $table;
33
        $this->primary = $primary;
34
        $describe = $this->describe();
35
        
36
        $this->check_fail();
37
38
        $this->mountData($describe);
39
        $this->full = true;
40
        return $this;
41
    }
42
43
    private function mountData(array $table): Datamanager
44
    {
45
        foreach ($table as $column) {
46
            $field = null;
47
            foreach ($column as $propriety => $value) {
48
                switch ($propriety) {
49
                    case 'Field':
50
                        $field = $value;
51
                        $this->$field = null;
52
                        break;
53
                    case 'Type':
54
                        $type = $value;
55
56
                        if(strpos($value,'(')){
57
                            switch (substr($value,0,strpos($value,'('))) {
58
                                case 'varchar':
59
                                case 'char':
60
                                case 'text': $type = 'string'; break;
61
                                case 'tinyint':
62
                                case 'mediumint':
63
                                case 'smallint':
64
                                case 'bigint':
65
                                case 'int': $type = 'int'; break;
66
                                case 'decimal':
67
                                case 'float':
68
                                case 'double':
69
                                case 'real': $type = 'float'; break;
70
                                default: $type = $value; break;
71
                            }
72
                        }
73
74
                        switch ($type) {
75
                            case 'string':
76
                            case 'float':
77
                            case 'int':
78
                                $this->$field = ['maxlength' => substr($value,(strpos($value,'(')+1),-1) ]; 
79
                                break;
80
                            case 'date':
81
                                $this->$field = ['maxlength' => 10];
82
                                break;
83
                            case 'datetime':
84
                                $this->$field = ['maxlength' => 19];
85
                                break;
86
                            case 'boolean':
87
                                $this->$field = ['maxlength' => 1];
88
                                break;
89
                            default:
90
                                $this->$field = ['maxlength' => null];
91
                                break;
92
                        }
93
94
                        $this->$field = ['type' => $type];
95
                        break;
96
                    case 'Null':
97
                        $this->$field = ['null' => ($value === 'YES') ? 1 : 0];
98
                        break;
99
                    case 'Key':
100
                        $this->$field = ['key' => $value];
101
                        $this->$field = ['upgradeable' => ($value == 'PRI') ? 0 : 1];
102
                        break;
103
                    case 'Extra':
104
                        $this->$field = ['extra' => $value];
105
                        break;
106
                    case 'Default':
107
                        $this->$field = ['default' => $value];
108
                        $this->$field = ['value' => null];
109
                        $this->$field = ['changed' => false];
110
                        $this->select[$field] = true;
111
                        break;
112
                }
113
            }
114
        }
115
        return $this;
116
    }
117
118
    public function __set(string $prop,$value): Datamanager
119
    {
120
121
        if(is_array($value)){
122
            $attr = array_keys($value)[0];
123
            $this->data[$prop][$attr] = $value[$attr];
124
            return $this;
125
        }
126
127
        if($this->full && !array_key_exists($prop,$this->data)){
128
            throw new Exception("{$prop} field does not exist in the table {$this->table}.");
129
        }
130
131
        $this->data[$prop]['changed'] = true;
132
        $this->data[$prop]['value'] = $value;
133
        
134
        return $this;
135
    }
136
137
    public function getData(): ?array
138
    {
139
        return $this->data;
140
    }
141
142
    public function __get(string $field)
143
    {
144
        if($this->full && !array_key_exists($field,$this->data)){
145
            throw new Exception("{$field} field does not exist in the table {$this->table}.");
146
        }
147
148
        return $this->data[$field]['value'];
149
    }
150
151
    public function getCount(): int
152
    {
153
        return $this->count;
154
    }
155
156
    public function except($deniable): Datamanager
157
    {
158
        $deniable = (is_array($deniable)) ? $deniable : [$deniable];
159
160
        foreach ($deniable as $field) {
161
            if(!array_key_exists($field,$this->data)){
162
                throw new Exception("{$field} field does not exist in the table {$this->table}.");
163
            }
164
165
            $this->excepts[$field] = true;
166
        }
167
168
        return $this;
169
    }
170
171
    public function deny(): Datamanager
172
    {
173
        foreach ($this->excepts as $field => $value) {
174
            unset($this->select[$field]);
175
        }
176
        return $this;
177
    }
178
179
    public function orderBy(string $field, string $ord = 'ASC'): Datamanager
180
    {
181
        if(!array_key_exists(str_replace(['asc','ASC','desc','DESC',' '],'',$field),$this->data) && $this->full){
182
            throw new Exception("{$field} field does not exist in the table {$this->table}.");
183
        }
184
185
        if(strpos(strtolower($field),'asc') || strpos(strtolower($field),'desc')){
186
            $ord = '';
187
        }
188
189
        $this->order = " ORDER BY {$field} {$ord} ";
190
        return $this;
191
    }
192
193
    public function only($params): Datamanager
194
    {
195
        $params = (is_array($params)) ? $params : [$params];
196
        $this->select = [];
197
198
        foreach ($params as $field) {
199
200
            if(!array_key_exists($field,$this->data) && $this->full){
201
                throw new Exception("{$field} field does not exist in the table {$this->table}.");
202
            }
203
204
            $this->select[$field] = true;
205
        }
206
        $this->select[$this->primary] = true;
207
208
        return $this;
209
    }
210
211
    public function where(array $where): Datamanager
212
    {
213
        $this->where['AND'] = (array_key_exists('AND',$this->where)) ?? '';
214
        $w = [];
215
        foreach ($where as $condition => $values) {
216
217
            if(!is_array($values)){
218
                $w['AND'][] = $values;
219
                continue;
220
            }
221
222
            $this->check_where_array($values);
223
224
            $w[(is_int($condition) ? 'AND' : $condition)][] = $values;
225
                       
226
        }
227
228
        $this->where = array_merge($this->where,$w);
229
230
        return $this;
231
    }
232
233
    public function check_where_array(array $where)
234
    {
235
        if(count($where) != 3){
236
            throw new Exception("Condition where set incorrectly: ".implode(' ',$where));
237
        }
238
239
        if(!array_key_exists($where[0],$this->data) && $this->full){
240
            throw new Exception("{$where[0]} field does not exist in the table {$this->table}.");
241
        }
242
    }
243
244
    public function limit(string $limit): Datamanager
245
    {
246
        $this->limit = $limit;
247
        return $this;
248
    }
249
250
    public function offset(int $offset): Datamanager
251
    {
252
        if(is_null($this->limit)){
253
            throw new Exception("The limit must be set before the offset.");
254
        }
255
256
        $this->offset = $offset;
257
        return $this;
258
    }
259
260
    public function result(): array
261
    {
262
        return $this->result;
263
    }
264
265
    public function first(): Datamanager
266
    {
267
        return  (count($this->result) > 0) ? $this->setByDatabase($this->result[0]) : $this;
268
    }
269
270
    public function setByDatabase(array $arrayValues): Datamanager
271
    {
272
        $clone = clone $this;
273
        
274
        $clone->result = [
275
            0 => $this->result[0]
276
        ];
277
278
        $clone->count = 1;
279
280
        foreach ($arrayValues as $key => $value) {
281
282
            if(!array_key_exists($key,$this->data)){
283
                throw new Exception("{$key} field does not exist in the table {$this->table}.");
284
            }
285
286
            $clone->data[$key]['value'] = $value;
287
        }
288
        return $clone;
289
    }
290
291
    public function toJson(): string
292
    {
293
        $string = '';
294
        foreach ($this->data as $key => $value) {
295
296
            if(gettype($value)==='object'){
297
                $value = $value->getData()[$this->primary]['value'];
298
            }
299
300
            $string .= '"'.$key.'"'.':"'.$value.'",';
301
        }
302
        return str_replace(',}', '}', '{'.$string.'}');
303
    }
304
305
    private function mountRemove(): array
306
    {
307
        $return = ['data' => '', 'where' => ''];
308
        foreach($this->where as $clause => $condition){
309
            if(strlen($clause) === 0){
310
                $return['where'] .= " {$clause} {$condition[0]} {$condition[1]} :q_{$condition[0]} ";
311
                $return['data'] .= "q_{$condition[0]}={$condition[2]}&";
312
                continue;
313
            }
314
                
315
            foreach($condition as $value){
316
                $return['where'] .= " {$clause} {$value[0]} {$value[1]} :q_{$value[0]} ";
317
                $return['data'] .= "q_{$value[0]}={$value[2]}&";
318
            }
319
        }
320
        return $return;
321
    }   
322
323
    public function remove(?bool $exec = false): Datamanager
324
    {
325
        if(!$exec){
0 ignored issues
show
Bug Best Practice introduced by
The expression $exec of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
326
            $this->clause = 'remove';    
327
            return $this;
328
        }
329
330
        $this->clause = null;
331
332
        if(count($this->where) == 1){
333
            $this->removeById();
334
            return $this;
335
        }
336
337
        $this->delete($this->mountRemove()['where'], substr( $this->mountRemove()['data'] ,0,-1) );
338
339
        $this->check_fail();
340
            
341
        return $this;
342
    }
343
344
    private function removeById(): bool
345
    {
346
        $delete = $this->delete("{$this->primary}=:{$this->primary}","{$this->primary}={$this->getData()[$this->primary]['value']}");
347
348
        $this->check_fail();
349
350
        return $delete;
351
    }
352
353
    public function check_primaryAuto()
354
    {
355
356
    }
357
358
    private function mountSave(): array
359
    {
360
        $return = ['data' => ''];
361
362
        foreach ($this->data as $key => $value) {
363
            if(strstr($this->data[$key]['extra'],'auto_increment') && $key !== $this->primary){
364
                continue;
365
            }
366
367
            if(($this->data[$key]['changed'] && $this->data[$key]['upgradeable']) || $this->primary === $key){
368
                $return['data'][$key] = $this->data[$key]['value'];
369
            }
370
        }
371
372
        return $return;
373
    }
374
375
    public function save(): Datamanager
376
    {
377
        
378
        $this->transaction('begin');
379
380
        try{
381
            $this->update(
382
                $this->mountSave()['data'],
0 ignored issues
show
Bug introduced by
$this->mountSave()['data'] of type string is incompatible with the type array expected by parameter $data of HnrAzevedo\Datamanager\Datamanager::update(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

382
                /** @scrutinizer ignore-type */ $this->mountSave()['data'],
Loading history...
383
                "{$this->primary}=:{$this->primary}", 
384
                $this->primary.'='.$this->getData()[$this->primary]['value']
385
            );
386
387
            $this->check_fail();
388
389
            $this->transaction('commit');
390
        }catch(Exception $er){
391
            $this->transaction('rollback');
392
            throw $er;
393
        }
394
395
        return $this;
396
    }
397
398
    public function persist(): Datamanager
399
    {
400
        $columns = '';
401
        $values = '';
402
        $data = [];
403
404
        foreach ($this->data as $key => $value) {
405
            if(strstr($this->data[$key]['extra'],'auto_increment')){
406
                continue;
407
            }
408
409
            if(strlen($value['value']) > $value['maxlength']){
410
                throw new Exception("The information provided for column {$key} of table {$this->table} exceeded that allowed.");
411
            }
412
413
            $columns .= $key.',';
414
            $values .= ':'.$key.',';
415
            $data[$key] = $value['value'];
416
        }
417
418
        $this->transaction('begin');
419
        try{
420
           
421
            $id = $this->insert($data);
422
423
            $this->check_fail();
424
425
            $this->getData()[$this->primary]['value'] = $id;
426
            
427
            $this->transaction('commit');
428
429
        }catch(Exception $er){
430
            $this->transaction('rollback');
431
            throw $er;
432
        }
433
434
        return $this;
435
    }
436
437
    public function toEntity()
438
    {
439
        if($this->getCount() === 0){
440
            return null;
441
        }
442
443
        $entity = $this->setByDatabase($this->result[0]);
444
445
        if(count($this->result) > 1){
446
            $entity = [];
447
            foreach ($this->result as $key => $value) {
448
                $entity[] = $this->setByDatabase($value);
449
            }
450
        }
451
452
        return $entity;
453
    }
454
455
    public function findById($id): Datamanager
456
    {
457
        $this->where([$this->primary,'=',$id]);
458
        return $this;
459
    }
460
461
    private function mountWhereExec(): array
462
    {
463
        $return = ['where' => '', 'data' => ''];
464
465
        foreach ($this->where as $key => $value) {
466
467
            $key = (!$key) ? '' : " {$key} ";
468
469
            if(is_array($value[0])){
470
471
                foreach ($value as $k => $v) {
472
                    $return['where'] .= " {$key} {$v[0]} {$v[1]} :q_{$v[0]} ";
473
                    $return['data']["q_{$v[0]}"] = $v[2];
474
                }
475
476
                continue;
477
            }
478
             
479
            $return['where'] .= " {$key} {$value[0]} {$value[1]} :q_{$value[0]} ";
480
            $return['data']["q_{$value[0]}"] = $value[2];
481
482
        }
483
        return $return;
484
    }
485
486
    public function execute(): Datamanager
487
    {
488
        if(!is_null($this->clause) && $this->clause == 'remove'){
489
            return $this->remove(true);
490
        }
491
492
        $this->deny();
493
        
494
        $this->mountSelect();
495
        
496
        $where = substr($this->mountWhereExec()['where'],0,-1);
497
        $this->query .= " WHERE {$where} ";
498
499
        $this->query .= $this->order;
500
       
501
        $this->mountLimit();
502
        $this->mountOffset();
503
504
        $this->result = $this->select($this->query, $this->mountWhereExec['data']);
0 ignored issues
show
Bug Best Practice introduced by
The property mountWhereExec does not exist on HnrAzevedo\Datamanager\Datamanager. Since you implemented __get, consider adding a @property annotation.
Loading history...
505
506
        $this->check_fail();
507
508
        $this->count = count($this->result);
509
        $this->query = null;
510
511
        return $this;
512
    }
513
514
    private function mountSelect()
515
    {
516
        $select = substr(implode(',',array_keys($this->select)), 0, -1);
517
518
        $this->query = str_replace('*', $select,$this->query);
519
    }
520
521
    private function mountLimit()
522
    {
523
        if(!is_null($this->limit)){
524
            $this->query .= " LIMIT {$this->limit}";
525
        }
526
    }
527
528
    private function mountOffset()
529
    {
530
        if(!is_null($this->offset)){
531
            $this->query .= " OFFSET {$this->offset}";
532
        }
533
    }
534
535
    public function find(?int $key = null): Datamanager
536
    {
537
        $this->query = " SELECT * FROM {$this->table} ";
538
539
        if(is_int($key)){
540
            return $this->findById($key);
541
        }
542
543
        return $this;
544
    }
545
546
}
547