Passed
Branch master (73ca69)
by Boudry
03:33
created

PdoHandlerDriver::deleteOneEntity()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 0
cts 11
cp 0
rs 9.2
c 0
b 0
f 0
cc 4
eloc 13
nc 6
nop 2
crap 20
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 16 and the first side effect is on line 239.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/*
3
    Condorcet PHP Class, with Schulze Methods and others !
4
5
    By Julien Boudry - MIT LICENSE (Please read LICENSE.txt)
6
    https://github.com/julien-boudry/Condorcet
7
*/
8
declare(strict_types=1);
9
10
namespace Condorcet\DataManager\DataHandlerDrivers;
11
12
use Condorcet\DataManager\DataHandlerDrivers\DataHandlerDriverInterface;
13
use Condorcet\CondorcetException;
14
use Condorcet\CondorcetVersion;
15
16
class PdoHandlerDriver implements DataHandlerDriverInterface
17
{
18
    use CondorcetVersion;
19
20
    protected const SEGMENT = [300,100,50,10,1];
21
22
    protected $_handler;
23
    protected $_transaction = false;
24
    protected $_queryError = false;
25
26
    // Database structure
27
    protected $_struct;
28
    // Prepare Query
29
    protected $_prepare = [];
30
    // Data CallBack function
31
    public $_dataContextObject;
32
33
34
    public function __construct (\PDO $bdd, bool $tryCreateTable = false, array $struct = ['tableName' => 'Entitys', 'primaryColumnName' => 'id', 'dataColumnName' => 'data'])
35
    {
36
        if (!$this->checkStructureTemplate($struct)) :
37
            throw new CondorcetException;
38
        endif;
39
40
        $this->_struct = $struct;
41
42
        $this->_handler = $bdd;
43
44
        $this->_handler->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
45
46
        if ($tryCreateTable) :
47
            $this->createTable();
48
        endif;
49
50
        $this->initPrepareQuery();
51
    }
52
53
    public function __destruct ()
54
    {
55
        if ($this->_queryError) :
56
            $this->_handler->rollback();
57
            $this->_transaction = false;
58
        else :
59
            $this->closeTransaction();
60
        endif;
61
    }
62
63
64
    // INTERNAL
65
66
    protected function checkStructureTemplate (array &$struct) : bool
67
    {
68
        if (    !empty($struct['tableName']) && !empty($struct['primaryColumnName']) && !empty($struct['dataColumnName']) &&
69
                is_string($struct['tableName']) && is_string($struct['primaryColumnName']) && is_string($struct['dataColumnName'])
70
         ) :
71
            return true;
72
        else :
73
            return false;
74
        endif;
75
    }
76
77
    public function createTable () : void
78
    {
79
        try {
80
            $this->_handler->exec('CREATE TABLE IF NOT EXISTS '.$this->_struct['tableName'].' ('.$this->_struct['primaryColumnName'].' INTEGER PRIMARY KEY NOT NULL , '.$this->_struct['dataColumnName'].' BLOB NOT NULL )');
81
        } catch (\Exception $e) {
82
            throw $e;
83
        }  
84
    }
85
86
    protected function initPrepareQuery () : void
87
    {
88
        // Base - Small query ends
89
        $template['end_template'] = ';';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$template was never initialized. Although not strictly required by PHP, it is generally a good practice to add $template = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
90
        $template['insert_template'] = 'INSERT INTO '.$this->_struct['tableName'].' ('.$this->_struct['primaryColumnName'].', '.$this->_struct['dataColumnName'].') VALUES ';
91
        $template['delete_template'] = 'DELETE FROM '.$this->_struct['tableName'].' WHERE '.$this->_struct['primaryColumnName'];
92
        $template['select_template'] = 'SELECT '.$this->_struct['primaryColumnName'].','.$this->_struct['dataColumnName'].' FROM '.$this->_struct['tableName'].' WHERE '.$this->_struct['primaryColumnName'];
93
        $template['update_template'] = 'UPDATE '.$this->_struct['tableName'].' SET '.$this->_struct['dataColumnName'].' = :data WHERE '.$this->_struct['primaryColumnName'];
94
95
        // Select the max / min key value. Usefull if array cursor is lost on DataManager.
96
        $this->_prepare['selectMaxKey'] = $this->_handler->prepare('SELECT max('.$this->_struct['primaryColumnName'].') FROM '.$this->_struct['tableName'] . $template['end_template']);
97
        $this->_prepare['selectMinKey'] = $this->_handler->prepare('SELECT min('.$this->_struct['primaryColumnName'].') FROM '.$this->_struct['tableName'] . $template['end_template']);
98
99
        // Insert many Entitys
100
            $makeMany = function ($how) use (&$template) {
101
                $query = $template['insert_template'];
102
                
103
                for ($i=1; $i < $how; $i++) :
104
                    $query .= '(:key'.$i.', :data'.$i.'),';
105
                endfor;
106
107
                $query .= '(:key'.$how.', :data'.$how.')' . $template['end_template'];
108
109
                return $query;
110
            };
111
112
            foreach (self::SEGMENT as $value) :
113
                $this->_prepare['insert'.$value.'Entitys'] = $this->_handler->prepare($makeMany($value));
114
            endforeach;
115
116
        // Delete one Entity
117
        $this->_prepare['deleteOneEntity'] = $this->_handler->prepare($template['delete_template'] . ' = ?' . $template['end_template']);
118
119
        // Get a Entity
120
        $this->_prepare['selectOneEntity'] = $this->_handler->prepare($template['select_template'] . ' = ?' . $template['end_template']);
121
122
        // Get a range of Entity
123
        $this->_prepare['selectRangeEntitys'] = $this->_handler->prepare($template['select_template'] . ' >= :startKey order by '.$this->_struct['primaryColumnName'].' asc LIMIT :limit' . $template['end_template']);
124
125
        // Count Entitys
126
        $this->_prepare['countEntitys'] = $this->_handler->prepare('SELECT count('.$this->_struct['primaryColumnName'].') FROM '. $this->_struct['tableName'] . $template['end_template']);
127
128
        // Update Entity
129
        $this->_prepare['updateOneEntity'] = $this->_handler->prepare($template['update_template'] . ' = :key' . $template['end_template']);
130
131
        // Flush All
132
        $this->_prepare['flushAll'] = $this->_handler->prepare($template['delete_template'] . ' is not null' . $template['end_template']);
133
    }
134
135
    protected function initTransaction () : void
136
    {
137
        if (!$this->_transaction) :
138
            $this->_transaction = $this->_handler->beginTransaction();
139
        endif;
140
    }
141
142
    public function closeTransaction () : void
143
    {
144
        if ($this->_transaction === true) :
145
            if ($this->_queryError) :
146
                throw new CondorcetException;
147
            endif;
148
149
            $this->_transaction = !$this->_handler->commit();
150
        endif;
151
    }
152
153
154
    // DATA MANAGER
155
    public function insertEntitys (array $input) : void
156
    {
157
        $this->sliceInput($input);
158
159
        try {
160
            $this->initTransaction();
161
162
            foreach ($input as $group) :
163
                $param = [];
164
                $i = 1;
165
                $group_count = count($group);
166
167
                foreach ($group as $key => &$Entity) :
168
                    $param['key'.$i] = $key;
169
                    $param['data'.$i++] = $this->_dataContextObject->dataPrepareStoringAndFormat($Entity);
170
                endforeach;
171
                unset($Entity);
172
173
                $this->_prepare['insert'.$group_count.'Entitys']->execute(
174
                    $param
175
                );
176
177
                if ($this->_prepare['insert'.$group_count.'Entitys']->rowCount() !== $group_count) :
178
                    throw new CondorcetException (0,'Tous les Entitys n\'ont pas été insérés');
179
                endif;
180
181
                $this->_prepare['insert'.$group_count.'Entitys']->closeCursor();
182
            endforeach;
183
184
            $this->closeTransaction();
185
        } catch (\Exception $e) {
186
            $this->_queryError = true;
187
            throw $e;
188
        }
189
    }
190
191
        protected function sliceInput (array &$input) : void
192
        {
193
            $count = count($input);
194
195
            foreach (self::SEGMENT as $value) :
196
                if ($count >= $value) :
197
                    $input = array_chunk($input, $value, true);
198
199
                    $end = end($input);
200
                    if (count($input) > 1 && count($end) < $value) :
201
                        $this->sliceInput($end);
202
                        unset($input[key($input)]);
203
                        $input = array_merge($input,$end);
204
                    endif;
205
                    break;
206
                endif;
207
            endforeach;
208
        }
209
210
    public function updateOneEntity (int $key,$data) : void
211
    {
212
        try {
213
            $this->_prepare['updateOneEntity']->bindParam(':key', $key, \PDO::PARAM_INT);
214
            $this->_prepare['updateOneEntity']->bindParam(':data', $data, \PDO::PARAM_STR);
215
216
            $this->_prepare['updateOneEntity']->execute();
217
218
            if ($this->_prepare['updateOneEntity']->rowCount() !== 1) :
219
                throw new CondorcetException (0,'Ce Entity n\'existe pas !');
220
            endif;
221
222
            $this->_prepare['updateOneEntity']->closeCursor();
223
        } catch (\Exception $e) {
224
            $this->_queryError = true;
225
            throw $e;
226
        }
227
    }
228
229
    public function deleteOneEntity (int $key, bool $justTry) : ?int
230
    {
231
        try {
232
            $this->_prepare['deleteOneEntity']->bindParam(1, $key, \PDO::PARAM_INT);
233
            $this->_prepare['deleteOneEntity']->execute();
234
235
            $deleteCount = $this->_prepare['deleteOneEntity']->rowCount();
236
237
            if (!$justTry && $deleteCount !== 1) :
238
                throw new CondorcetException (30);
239
            endif;
240
241
            $this->_prepare['deleteOneEntity']->closeCursor();
1 ignored issue
show
Coding Style introduced by
The visibility should be declared for property $this.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
242
243
            return $deleteCount;
1 ignored issue
show
Coding Style introduced by
The visibility should be declared for property $deleteCount.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
244
        } catch (\Exception $e) {
2 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
Coding Style introduced by
The visibility should be declared for property $e.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
245
            $this->_queryError = true;
246
            throw $e;
1 ignored issue
show
Coding Style introduced by
The visibility should be declared for property $e.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
247
        }
248
    }
249
250
    public function selectMaxKey () : ?int
251
    {
252
        if ($this->countEntitys() === 0) :
253
            return null;
254
        endif;
255
256
        try {
257
            $this->_prepare['selectMaxKey']->execute();
1 ignored issue
show
Coding Style introduced by
The visibility should be declared for property $this.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
258
            $r = (int) $this->_prepare['selectMaxKey']->fetch(\PDO::FETCH_NUM)[0];
2 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
Coding Style introduced by
The visibility should be declared for property $r.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
259
            $this->_prepare['selectMaxKey']->closeCursor();
1 ignored issue
show
Coding Style introduced by
The visibility should be declared for property $this.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
260
261
            return $r;
1 ignored issue
show
Coding Style introduced by
The visibility should be declared for property $r.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
262
        } catch (\Exception $e) {
2 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
Coding Style introduced by
The visibility should be declared for property $e.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
263
            throw $e;
264
        }
265
    }
266
267
    public function selectMinKey () : int
268
    {
269
        try {
270
            $this->_prepare['selectMinKey']->execute();
271
            $r = (int) $this->_prepare['selectMinKey']->fetch(\PDO::FETCH_NUM)[0];
272
            $this->_prepare['selectMinKey']->closeCursor();
273
274
            return $r;
275
        } catch (\Exception $e) {
276
            throw $e;
277
        }
278
    }
279
280
    public function countEntitys () : int
281
    {
282
        try {
283
            $this->_prepare['countEntitys']->execute();
284
            $r = (int) $this->_prepare['countEntitys']->fetch(\PDO::FETCH_NUM)[0];
285
            $this->_prepare['countEntitys']->closeCursor();
286
287
            return $r;
288
        } catch (\Exception $e) {
289
            throw $e;
290
        }
291
    }
292
293
    // return false if Entity does not exist.
294
    public function selectOneEntity (int $key)
295
    {
296
        try {
297
            $this->_prepare['selectOneEntity']->bindParam(1, $key, \PDO::PARAM_INT);
298
            $this->_prepare['selectOneEntity']->execute();
299
            
300
            $r = $this->_prepare['selectOneEntity']->fetchAll(\PDO::FETCH_NUM);
301
            $this->_prepare['selectOneEntity']->closeCursor();
302
            if (!empty($r)) :
303
                return $this->_dataContextObject->dataCallBack( $r[0][1] );
304
            else :
305
                return false;
306
            endif;
307
        } catch (\Exception $e) {
308
            throw $e;
309
        }
310
    }
311
312
    public function selectRangeEntitys (int $key, int $limit) : array
313
    {
314
        try {
315
            $this->_prepare['selectRangeEntitys']->bindParam(':startKey', $key, \PDO::PARAM_INT);
316
            $this->_prepare['selectRangeEntitys']->bindParam(':limit', $limit, \PDO::PARAM_INT);
317
            $this->_prepare['selectRangeEntitys']->execute();
318
            
319
            $r = $this->_prepare['selectRangeEntitys']->fetchAll(\PDO::FETCH_NUM);
320
            $this->_prepare['selectRangeEntitys']->closeCursor();
321
            if (!empty($r)) :
322
                $result = [];
323
                foreach ($r as $value) :
324
                    $result[(int) $value[0]] = $this->_dataContextObject->dataCallBack( $value[1] );
325
                endforeach ;
326
327
                return $result;
328
            else :
329
                return [];
330
            endif;
331
        } catch (\Exception $e) {
332
            throw $e;
333
        }
334
    }
335
336
    public function flushAll () : ?int
337
    {
338
        try {
339
            $this->_prepare['flushAll']->execute();
340
            $r = $this->_prepare['flushAll']->rowCount();
341
342
            $this->_prepare['flushAll']->closeCursor();
343
344
            return $r;
345
        } catch (\Exception $e) {
346
            $this->_queryError = true;
347
            throw $e;
348
        }      
349
    }
350
351
}