PdoHandlerDriver::selectOneEntity()   A
last analyzed

Complexity

Conditions 3
Paths 7

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.243

Importance

Changes 0
Metric Value
cc 3
nc 7
nop 1
dl 0
loc 17
ccs 7
cts 10
cp 0.7
crap 3.243
rs 9.7
c 0
b 0
f 0
1
<?php
2
/*
3
    PDO DataHandler Module - From the original Condorcet PHP
4
5
    Condorcet PHP - Election manager and results calculator.
6
    Designed for the Condorcet method. Integrating a large number of algorithms extending Condorcet. Expandable for all types of voting systems.
7
8
    By Julien Boudry and contributors - MIT LICENSE (Please read LICENSE.txt)
9
    https://github.com/julien-boudry/Condorcet
10
*/
11
declare(strict_types=1);
12
13
namespace CondorcetPHP\Condorcet\DataManager\DataHandlerDrivers;
14
15
use CondorcetPHP\Condorcet\CondorcetVersion;
16
use CondorcetPHP\Condorcet\Throwable\{CondorcetException, CondorcetInternalError};
17
18
class PdoHandlerDriver implements DataHandlerDriverInterface
19
{
20
    use CondorcetVersion;
21
22
    protected const SEGMENT = [300,100,50,10,1];
23
24
    protected $_handler;
25
    protected $_transaction = false;
26
    protected $_queryError = false;
27
28
    // Database structure
29
    protected $_struct;
30
    // Prepare Query
31
    protected $_prepare = [];
32
    // Data CallBack function
33
    public $_dataContextObject;
34
35
36 7
    public function __construct (\PDO $bdd, bool $tryCreateTable = false, array $struct = ['tableName' => 'Entitys', 'primaryColumnName' => 'id', 'dataColumnName' => 'data'])
37
    {
38 7
        if (!$this->checkStructureTemplate($struct)) :
39
            throw new CondorcetException;
40
        endif;
41
42 7
        $this->_struct = $struct;
43
44 7
        $this->_handler = $bdd;
45
46 7
        $this->_handler->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
47
48 7
        if ($tryCreateTable) :
49 7
            $this->createTable();
50
        endif;
51
52 7
        $this->initPrepareQuery();
53 7
    }
54
55 2
    public function __destruct ()
56
    {
57 2
        if ($this->_queryError) :
58
            $this->_handler->rollback();
59
            $this->_transaction = false;
60
        else :
61 2
            $this->closeTransaction();
62
        endif;
63 2
    }
64
65
66
    // INTERNAL
67
68 7
    protected function checkStructureTemplate (array &$struct) : bool
69
    {
70 7
        if (    !empty($struct['tableName']) && !empty($struct['primaryColumnName']) && !empty($struct['dataColumnName']) &&
71 7
                is_string($struct['tableName']) && is_string($struct['primaryColumnName']) && is_string($struct['dataColumnName'])
72
         ) :
73 7
            return true;
74
        else :
75
            return false;
76
        endif;
77
    }
78
79 7
    public function createTable () : void
80
    {
81
        try {
82 7
            $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 )');
83
        } catch (\Exception $e) {
84
            throw $e;
85
        }  
86 7
    }
87
88 7
    protected function initPrepareQuery () : void
89
    {
90 7
        $template = [];
91
92
        // Base - Small query ends
93 7
        $template['end_template'] = ';';
94 7
        $template['insert_template'] = 'INSERT INTO '.$this->_struct['tableName'].' ('.$this->_struct['primaryColumnName'].', '.$this->_struct['dataColumnName'].') VALUES ';
95 7
        $template['delete_template'] = 'DELETE FROM '.$this->_struct['tableName'].' WHERE '.$this->_struct['primaryColumnName'];
96 7
        $template['select_template'] = 'SELECT '.$this->_struct['primaryColumnName'].','.$this->_struct['dataColumnName'].' FROM '.$this->_struct['tableName'].' WHERE '.$this->_struct['primaryColumnName'];
97
98
        // Select the max / min key value. Usefull if array cursor is lost on DataManager.
99 7
        $this->_prepare['selectMaxKey'] = $this->_handler->prepare('SELECT max('.$this->_struct['primaryColumnName'].') FROM '.$this->_struct['tableName'] . $template['end_template']);
100 7
        $this->_prepare['selectMinKey'] = $this->_handler->prepare('SELECT min('.$this->_struct['primaryColumnName'].') FROM '.$this->_struct['tableName'] . $template['end_template']);
101
102
        // Insert many Entitys
103
            $makeMany = function ($how) use (&$template) {
104 7
                $query = $template['insert_template'];
105
                
106 7
                for ($i=1; $i < $how; $i++) :
107 7
                    $query .= '(:key'.$i.', :data'.$i.'),';
108
                endfor;
109
110 7
                $query .= '(:key'.$how.', :data'.$how.')' . $template['end_template'];
111
112 7
                return $query;
113 7
            };
114
115 7
            foreach (self::SEGMENT as $value) :
116 7
                $this->_prepare['insert'.$value.'Entitys'] = $this->_handler->prepare($makeMany($value));
117
            endforeach;
118
119
        // Delete one Entity
120 7
        $this->_prepare['deleteOneEntity'] = $this->_handler->prepare($template['delete_template'] . ' = ?' . $template['end_template']);
121
122
        // Get a Entity
123 7
        $this->_prepare['selectOneEntity'] = $this->_handler->prepare($template['select_template'] . ' = ?' . $template['end_template']);
124
125
        // Get a range of Entity
126 7
        $this->_prepare['selectRangeEntitys'] = $this->_handler->prepare($template['select_template'] . ' >= :startKey order by '.$this->_struct['primaryColumnName'].' asc LIMIT :limit' . $template['end_template']);
127
128
        // Count Entitys
129 7
        $this->_prepare['countEntitys'] = $this->_handler->prepare('SELECT count('.$this->_struct['primaryColumnName'].') FROM '. $this->_struct['tableName'] . $template['end_template']);
130 7
    }
131
132 6
    protected function initTransaction () : void
133
    {
134 6
        if (!$this->_transaction) :
135 6
            $this->_transaction = $this->_handler->beginTransaction();
136
        endif;
137 6
    }
138
139 7
    public function closeTransaction () : void
140
    {
141 7
        if ($this->_transaction === true) :
142 6
            if ($this->_queryError) :
143
                throw new CondorcetInternalError ('Query Error.');
144
            endif;
145
146 6
            $this->_transaction = !$this->_handler->commit();
147
        endif;
148 7
    }
149
150
151
    // DATA MANAGER
152 6
    public function insertEntitys (array $input) : void
153
    {
154 6
        $this->sliceInput($input);
155
156
        try {
157 6
            $this->initTransaction();
158
159 6
            foreach ($input as $group) :
160 6
                $param = [];
161 6
                $i = 1;
162 6
                $group_count = count($group);
163
164 6
                foreach ($group as $key => &$Entity) :
165 6
                    $param['key'.$i] = $key;
166 6
                    $param['data'.$i++] = $this->_dataContextObject->dataPrepareStoringAndFormat($Entity);
167
                endforeach;
168 6
                unset($Entity);
169
170 6
                $this->_prepare['insert'.$group_count.'Entitys']->execute(
171 6
                    $param
172
                );
173
174 6
                if ($this->_prepare['insert'.$group_count.'Entitys']->rowCount() !== $group_count) :
175
                    throw new CondorcetInternalError ('Tous les Entitys n\'ont pas été insérés');
176
                endif;
177
178 6
                $this->_prepare['insert'.$group_count.'Entitys']->closeCursor();
179
            endforeach;
180
181 6
            $this->closeTransaction();
182
        } catch (\Throwable $e) {
183
            $this->_queryError = true;
184
            throw $e;
185
        }
186 6
    }
187
188 6
        protected function sliceInput (array &$input) : void
189
        {
190 6
            $count = count($input);
191
192 6
            foreach (self::SEGMENT as $value) :
193 6
                if ($count >= $value) :
194 6
                    $input = array_chunk($input, $value, true);
195
196 6
                    $end = end($input);
197 6
                    if (count($input) > 1 && count($end) < $value) :
198 1
                        $this->sliceInput($end);
199 1
                        unset($input[key($input)]);
200 1
                        $input = array_merge($input,$end);
201
                    endif;
202 6
                    break;
203
                endif;
204
            endforeach;
205 6
        }
206
207
208 2
    public function deleteOneEntity (int $key, bool $justTry) : ?int
209
    {
210
        try {
211 2
            $this->_prepare['deleteOneEntity']->bindParam(1, $key, \PDO::PARAM_INT);
212 2
            $this->_prepare['deleteOneEntity']->execute();
213
214 2
            $deleteCount = $this->_prepare['deleteOneEntity']->rowCount();
215
216 2
            if (!$justTry && $deleteCount !== 1) :
217
                throw new CondorcetInternalError ('Entity deletion failure.');
218
            endif;
219
220 2
            $this->_prepare['deleteOneEntity']->closeCursor();
221
222 2
            return $deleteCount;
223
        } catch (\Exception $e) {
224
            $this->_queryError = true;
225
            throw $e;
226
        }
227
    }
228
229 1
    public function selectMaxKey () : ?int
230
    {
231 1
        if ($this->countEntitys() === 0) :
232
            return null;
233
        endif;
234
235
        try {
236 1
            $this->_prepare['selectMaxKey']->execute();
237 1
            $r = (int) $this->_prepare['selectMaxKey']->fetch(\PDO::FETCH_NUM)[0];
238 1
            $this->_prepare['selectMaxKey']->closeCursor();
239
240 1
            return $r;
241
        } catch (\Exception $e) {
242
            throw $e;
243
        }
244
    }
245
246 4
    public function selectMinKey () : int
247
    {
248
        try {
249 4
            $this->_prepare['selectMinKey']->execute();
250 4
            $r = (int) $this->_prepare['selectMinKey']->fetch(\PDO::FETCH_NUM)[0];
251 4
            $this->_prepare['selectMinKey']->closeCursor();
252
253 4
            return $r;
254
        } catch (\Exception $e) {
255
            throw $e;
256
        }
257
    }
258
259 7
    public function countEntitys () : int
260
    {
261
        try {
262 7
            $this->_prepare['countEntitys']->execute();
263 7
            $r = (int) $this->_prepare['countEntitys']->fetch(\PDO::FETCH_NUM)[0];
264 7
            $this->_prepare['countEntitys']->closeCursor();
265
266 7
            return $r;
267
        } catch (\Exception $e) {
268
            throw $e;
269
        }
270
    }
271
272
    // return false if Entity does not exist.
273 4
    public function selectOneEntity (int $key)
274
    {
275
        try {
276 4
            $this->_prepare['selectOneEntity']->bindParam(1, $key, \PDO::PARAM_INT);
277 4
            $this->_prepare['selectOneEntity']->execute();
278
            
279 4
            $r = $this->_prepare['selectOneEntity']->fetchAll(\PDO::FETCH_NUM);
280 4
            $this->_prepare['selectOneEntity']->closeCursor();
281 4
            if (!empty($r)) :
282 4
                return $this->_dataContextObject->dataCallBack( $r[0][1] );
283
            else :
284
                return false;
285
            endif;
286
        } catch (\Exception $e) {
287
            throw $e;
288
        }
289
    }
290
291 5
    public function selectRangeEntitys (int $key, int $limit) : array
292
    {
293
        try {
294 5
            $this->_prepare['selectRangeEntitys']->bindParam(':startKey', $key, \PDO::PARAM_INT);
295 5
            $this->_prepare['selectRangeEntitys']->bindParam(':limit', $limit, \PDO::PARAM_INT);
296 5
            $this->_prepare['selectRangeEntitys']->execute();
297
            
298 5
            $r = $this->_prepare['selectRangeEntitys']->fetchAll(\PDO::FETCH_NUM);
299 5
            $this->_prepare['selectRangeEntitys']->closeCursor();
300 5
            if (!empty($r)) :
301 5
                $result = [];
302 5
                foreach ($r as $value) :
303 5
                    $result[(int) $value[0]] = $this->_dataContextObject->dataCallBack( $value[1] );
304
                endforeach ;
305
306 5
                return $result;
307
            else :
308
                return [];
309
            endif;
310
        } catch (\Exception $e) {
311
            throw $e;
312
        }
313
    }
314
}
315