Completed
Push — master ( 7e3127...a09c74 )
by Gabor
04:27
created

PDOAdapter   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 287
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 7
Bugs 3 Features 2
Metric Value
c 7
b 3
f 2
dl 0
loc 287
wmc 27
lcom 1
cbo 2
ccs 44
cts 44
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getDataStorage() 0 4 1
A setDataGroup() 0 10 2
A setIdKey() 0 10 2
A __construct() 0 20 3
A getData() 0 11 1
A getDataSet() 0 11 1
A getDataCardinality() 0 11 1
A getSelectQueryForExpression() 0 18 2
A getWhereExpression() 0 18 4
B saveData() 0 29 5
A bindValuesToStatement() 0 14 4
A deleteData() 0 6 1
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 PDO;
15
use PDOStatement;
16
use WebHemi\Adapter\Data\DataAdapterInterface;
17
use WebHemi\Adapter\Exception\InitException;
18
use WebHemi\Adapter\Exception\InvalidArgumentException;
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 InitException
76
     *
77
     * @return PDOAdapter
78
     */
79 4
    public function setDataGroup($dataGroup)
80
    {
81 4
        if (!empty($this->dataGroup)) {
82 1
            throw new InitException('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 InitException
96
     *
97
     * @return PDOAdapter
98
     */
99 1
    public function setIdKey($idKey)
100
    {
101 1
        if (!empty($this->idKey)) {
102 1
            throw new InitException('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%']
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