Passed
Push — master ( d26101...1eb4af )
by Gabor
03:11
created

PDOAdapter::saveData()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 29
c 0
b 0
f 0
ccs 0
cts 0
cp 0
rs 8.439
cc 5
eloc 18
nc 16
nop 2
crap 30
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 5.6
6
 *
7
 * @copyright 2012 - 2016 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
namespace WebHemi\Adapter\Data\PDO;
13
14
use InvalidArgumentException;
15
use PDO;
16
use PDOStatement;
17
use RuntimeException;
18
use WebHemi\Adapter\Data\DataAdapterInterface;
19
20
/**
21
 * Class PDOAdapter.
22
 */
23
class PDOAdapter implements DataAdapterInterface
24
{
25
    /** @var PDO */
26
    private $dataStorage;
27
    /** @var string */
28
    private $dataGroup = null;
29
    /** @var string */
30
    private $idKey = null;
31
32
    /**
33
     * PDOAdapter constructor.
34
     *
35
     * @param PDO $dataStorage
36
     *
37
     * @throws InvalidArgumentException
38
     */
39 10
    public function __construct($dataStorage = null)
40
    {
41 10
        if (!$dataStorage instanceof PDO) {
42 1
            $type = gettype($dataStorage);
43
44 1
            if ($type == 'object') {
45 1
                $type = get_class($dataStorage);
46 1
            }
47
48 1
            $message = sprintf(
49 1
                'Can\'t create %s instance. The parameter must be an instance of PDO, %s given.',
50 1
                __CLASS__,
51
                $type
52 1
            );
53
54 1
            throw new InvalidArgumentException($message);
55
        }
56
57 10
        $this->dataStorage = $dataStorage;
58 10
    }
59
60
    /**
61
     * Returns the Data Storage instance.
62
     *
63
     * @return PDO
64
     */
65 1
    public function getDataStorage()
66
    {
67 1
        return $this->dataStorage;
68
    }
69
70
    /**
71
     * Set adapter data group. For Databases this can be the Tables.
72
     *
73
     * @param string $dataGroup
74
     *
75
     * @throws RuntimeException
76
     *
77
     * @return PDOAdapter
78
     */
79 4
    public function setDataGroup($dataGroup)
80
    {
81 4
        if (!empty($this->dataGroup)) {
82 1
            throw new RuntimeException('Can\'t re-initialize dataGroup property. Property is already set.');
83
        }
84
85 4
        $this->dataGroup = $dataGroup;
86
87 4
        return $this;
88
    }
89
90
    /**
91
     * Set adapter ID key. For Databases this can be the Primary key. Only simple key is allowed.
92
     *
93
     * @param string $idKey
94
     *
95
     * @throws RuntimeException
96
     *
97
     * @return PDOAdapter
98
     */
99 1
    public function setIdKey($idKey)
100
    {
101 1
        if (!empty($this->idKey)) {
102 1
            throw new RuntimeException('Can\'t re-initialize idKey property. Property is already set.');
103
        }
104
105 1
        $this->idKey = $idKey;
106
107 1
        return $this;
108
    }
109
110
    /**
111
     * Get exactly one "row" of data according to the expression.
112
     *
113
     * @param mixed $identifier
114
     *
115
     * @return array
116
     *
117
     * @codeCoverageIgnore Don't test external library.
118
     */
119
    public function getData($identifier)
120
    {
121
        $queryBind = [];
122
123
        $query = $this->getSelectQueryForExpression([$this->idKey => $identifier], $queryBind, 1, 0);
124
        $statement = $this->getDataStorage()->prepare($query);
125
        $this->bindValuesToStatement($statement, $queryBind);
126
        $statement->execute();
127
128
        return $statement->fetch(PDO::FETCH_ASSOC);
129
    }
130
131
    /**
132
     * Get a set of data according to the expression and the chunk.
133
     *
134
     * @param array $expression
135
     * @param int   $limit
136
     * @param int   $offset
137
     *
138
     * @return array
139
     *
140
     * @codeCoverageIgnore Don't test external library.
141
     */
142
    public function getDataSet(array $expression, $limit = PHP_INT_MAX, $offset = 0)
143
    {
144
        $queryBind = [];
145
146
        $query = $this->getSelectQueryForExpression($expression, $queryBind, $limit, $offset);
147
        $statement = $this->getDataStorage()->prepare($query);
148
        $this->bindValuesToStatement($statement, $queryBind);
149
        $statement->execute();
150
151
        return $statement->fetchAll(PDO::FETCH_ASSOC);
152
    }
153
154
    /**
155
     * Get the number of matched data in the set according to the expression.
156
     *
157
     * @param array $expression
158
     *
159
     * @return int
160
     *
161
     * @codeCoverageIgnore Don't test external library.
162
     */
163
    public function getDataCardinality(array $expression)
164
    {
165
        $queryBind = [];
166
167
        $query = $this->getSelectQueryForExpression($expression, $queryBind);
168
        $statement = $this->getDataStorage()->prepare($query);
169
        $this->bindValuesToStatement($statement, $queryBind);
170
        $statement->execute();
171
172
        return $statement->rowCount();
173
    }
174
175
    /**
176
     * Builds SQL query from the expression.
177
     *
178
     * @param array $expression
179
     * @param array $queryBind
180
     * @param int   $limit
181
     * @param int   $offset
182
     *
183
     * @return string
184
     */
185 3
    private function getSelectQueryForExpression(
186
        array $expression,
187
        array &$queryBind,
188
        $limit = self::DATA_SET_RECORD_LIMIT,
189
        $offset = 0
190
    ) {
191 3
        $query = "SELECT * FROM {$this->dataGroup}";
192
193
        // Prepare WHERE expression.
194 3
        if (!empty($expression)) {
195 2
            $query .= $this->getWhereExpression($expression, $queryBind);
196 2
        }
197
198 3
        $query .= " LIMIT {$limit}";
199 3
        $query .= " OFFSET {$offset}";
200
201 3
        return $query;
202
    }
203
204
    /**
205
     * Creates a WHERE expression for the SQL query.
206
     *
207
     * @param array $expression
208
     * @param array $queryBind
209
     *
210
     * @return string
211
     */
212 5
    private function getWhereExpression(array $expression, array &$queryBind)
213
    {
214 5
        $whereExpression = '';
215 5
        $queryParams = [];
216
217 5
        foreach ($expression as $column => $value) {
218
            // allow special cases
219
            // @example  ['my_column LIKE ?' => 'some value%']
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
220 4
            $queryParams[] = strpos($column, '?') === false ? "{$column}=?" : $column;
221 4
            $queryBind[] = $value;
222 5
        }
223
224 5
        if (!empty($queryParams)) {
225 4
            $whereExpression = ' WHERE '.implode(' AND ', $queryParams);
226 4
        }
227
228 5
        return $whereExpression;
229
    }
230
231
    /**
232
     * Insert or update entity in the storage.
233
     *
234
     * @param mixed $identifier
235
     * @param array $data
236
     *
237
     * @return mixed The ID of the saved entity in the storage
238
     *
239
     * @codeCoverageIgnore Don't test external library.
240
     */
241
    public function saveData($identifier, array $data)
242
    {
243
        if (empty($identifier)) {
244
            $query = "INSERT INTO {$this->dataGroup}";
245
        } else {
246
            $query = "UPDATE {$this->dataGroup}";
247
        }
248
249
        $queryData = [];
250
        $queryBind = [];
251
252
        foreach ($data as $fieldName => $value) {
253
            $queryData[] = "{$fieldName}=?";
254
            $queryBind[] = $value;
255
        }
256
257
        $query .= ' SET '.implode(', ', $queryData);
258
259
        if (!empty($identifier)) {
260
            $query .= " WHERE {$this->idKey}=?";
261
            $queryBind[] = $identifier;
262
        }
263
264
        $statement = $this->getDataStorage()->prepare($query);
265
        $this->bindValuesToStatement($statement, $queryBind);
266
        $statement->execute();
267
268
        return empty($identifier) ? $this->getDataStorage()->lastInsertId() : $identifier;
269
    }
270
271
    /**
272
     * Binds values to the statement.
273
     *
274
     * @param PDOStatement $statement
275
     * @param array        $queryBind
276
     *
277
     * @codeCoverageIgnore Don't test external library.
278
     */
279
    private function bindValuesToStatement(PDOStatement &$statement, array $queryBind)
280
    {
281
        foreach ($queryBind as $index => $data) {
282
            $paramType = PDO::PARAM_STR;
283
284
            if (is_null($data)) {
285
                $paramType = PDO::PARAM_NULL;
286
            } elseif (is_numeric($data)) {
287
                $paramType = PDO::PARAM_INT;
288
            }
289
290
            $statement->bindValue($index + 1, $data, $paramType);
291
        }
292
    }
293
294
    /**
295
     * Removes an entity from the storage.
296
     *
297
     * @param mixed $identifier
298
     *
299
     * @return bool
300
     *
301
     * @codeCoverageIgnore Don't test external library.
302
     */
303
    public function deleteData($identifier)
304
    {
305
        $statement = $this->getDataStorage()->prepare("DELETE FROM WHERE {$this->idKey}=?");
306
307
        return $statement->execute([$identifier]);
308
    }
309
}
310