Completed
Push — master ( 22766a...b8b4d0 )
by Gabor
03:58
created

PDOAdapter::getStatementForExpression()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 3
Bugs 2 Features 0
Metric Value
c 3
b 2
f 0
dl 0
loc 34
ccs 0
cts 22
cp 0
rs 8.439
cc 6
eloc 17
nc 6
nop 3
crap 42
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
    public function __construct($dataStorage = null)
40
    {
41
        if (!$dataStorage instanceof PDO) {
42
            $type = gettype($dataStorage);
43
44
            if ($type == 'object') {
45
                $type = get_class($dataStorage);
46
            }
47
48
            $message = sprintf(
49
                'Can\'t create %s instance. The parameter must be an instance of PDO, %s given.',
50
                __CLASS__,
51
                $type
52
            );
53
54
            throw new InvalidArgumentException($message);
55
        }
56
57
        $this->dataStorage = $dataStorage;
58
    }
59
60
    /**
61
     * Returns the Data Storage instance.
62
     *
63
     * @return PDO
64
     */
65
    public function getDataStorage()
66
    {
67
        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
    public function setDataGroup($dataGroup)
80
    {
81
        if (!empty($this->dataGroup)) {
82
            throw new InitException('Can\'t re-initialize dataGroup property. Property is already set.');
83
        }
84
85
        $this->dataGroup = $dataGroup;
86
87
        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
    public function setIdKey($idKey)
100
    {
101
        if (!empty($this->idKey)) {
102
            throw new InitException('Can\'t re-initialize idKey property. Property is already set.');
103
        }
104
105
        $this->idKey = $idKey;
106
107
        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
    public function getData($identifier)
118
    {
119
        $statement = $this->getDataStorage()->prepare("SELECT * FROM {$this->dataGroup} WHERE {$this->idKey}=?");
120
        $statement->execute([$identifier]);
121
122
        return $statement->fetch(PDO::FETCH_ASSOC);
123
    }
124
125
    /**
126
     * Get a set of data according to the expression and the chunk.
127
     *
128
     * @param array $expression
129
     * @param int   $limit
130
     * @param int   $offset
131
     *
132
     * @return array
133
     */
134
    public function getDataSet(array $expression, $limit = null, $offset = null)
135
    {
136
        $statement = $this->getStatementForExpression($expression, $limit, $offset);
137
        $statement->execute();
138
139
        return $statement->fetchAll(PDO::FETCH_ASSOC);
140
    }
141
142
    /**
143
     * Get the number of matched data in the set according to the expression.
144
     *
145
     * @param array $expression
146
     *
147
     * @return int
148
     */
149
    public function getDataCardinality(array $expression)
150
    {
151
        $statement = $this->getStatementForExpression($expression);
152
        $statement->execute();
153
154
        return $statement->rowCount();
155
    }
156
157
    /**
158
     * Build query statement from the expression.
159
     *
160
     * @param array $expression
161
     * @param int   $limit
162
     * @param int   $offset
163
     *
164
     * @return PDOStatement
165
     */
166
    private function getStatementForExpression(array $expression, $limit = null, $offset = null)
167
    {
168
        $query = "SELECT * FROM {$this->dataGroup}";
169
        $queryParams = [];
170
        $queryBind = [];
171
172
        // Prepare WHERE expression.
173
        if (!empty($expression)) {
174
            $query .= ' WHERE ';
175
176
            foreach ($expression as $column => $value) {
177
                // allow special cases
178
                // @example  ['my_column LIKE ?' => 'some value%']
179
                $queryParams[] = strpos($column, '?') === false ? "{$column}=?" : $column;
180
                $queryBind[] = $value;
181
            }
182
183
            $query .= implode(' AND ', $queryParams);
184
        }
185
186
        // Prepare LIMIT and OFFSET
187
        if (!empty($limit)) {
188
            $query .= " LIMIT {$limit}";
189
190
            if (!is_null($offset)) {
191
                $query .= " OFFSET {$offset}";
192
            }
193
        }
194
195
        $statement = $this->getDataStorage()->prepare($query);
196
        $this->bindValuesToStatement($statement, $queryBind);
197
198
        return $statement;
199
    }
200
201
    /**
202
     * Insert or update entity in the storage.
203
     *
204
     * @param mixed $identifier
205
     * @param array $data
206
     *
207
     * @return mixed The ID of the saved entity in the storage
208
     */
209
    public function saveData($identifier, array $data)
210
    {
211
        if (empty($identifier)) {
212
            $query = "INSERT INTO {$this->dataGroup}";
213
        } else {
214
            $query = "UPDATE {$this->dataGroup}";
215
        }
216
217
        $queryData = [];
218
        $queryBind = [];
219
220
        foreach ($data as $fieldName => $value) {
221
            $queryData[] = "{$fieldName}=?";
222
            $queryBind[] = $value;
223
        }
224
225
        $query .= ' SET '.implode(', ', $queryData);
226
227
        if (!empty($identifier)) {
228
            $query .= " WHERE {$this->idKey}=?";
229
            $queryBind[] = $identifier;
230
        }
231
232
        $statement = $this->getDataStorage()->prepare($query);
233
        $this->bindValuesToStatement($statement, $queryBind);
234
        $statement->execute();
235
236
        return empty($identifier) ? $this->getDataStorage()->lastInsertId() : $identifier;
237
    }
238
239
    /**
240
     * Binds values to the statement.
241
     *
242
     * @param PDOStatement $statement
243
     * @param array        $queryBind
244
     */
245
    private function bindValuesToStatement(PDOStatement &$statement, array $queryBind)
246
    {
247
        foreach ($queryBind as $index => $data) {
248
            $paramType = PDO::PARAM_STR;
249
250
            if (is_null($data)) {
251
                $paramType = PDO::PARAM_NULL;
252
            } elseif (is_numeric($data)) {
253
                $paramType = PDO::PARAM_INT;
254
            }
255
256
            $statement->bindValue($index + 1, $data, $paramType);
257
        }
258
    }
259
260
    /**
261
     * Removes an entity from the storage.
262
     *
263
     * @param mixed $identifier
264
     *
265
     * @return bool
266
     */
267
    public function deleteData($identifier)
268
    {
269
        $statement = $this->getDataStorage()->prepare("DELETE FROM WHERE {$this->idKey}=?");
270
271
        return $statement->execute([$identifier]);
272
    }
273
}
274