Passed
Branch master (754829)
by Herberto
03:42 queued 01:55
created

PdoClient::createInsertSql()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 2
crap 1
1
<?php
2
namespace Hgraca\MicroOrm\DataSource\Pdo;
3
4
use Hgraca\MicroOrm\DataSource\ClientInterface;
5
use Hgraca\MicroOrm\DataSource\Exception\BindingMicroOrmException;
6
use Hgraca\MicroOrm\DataSource\Exception\ExecutionMicroOrmException;
7
use Hgraca\MicroOrm\DataSource\Exception\TypeResolutionMicroOrmException;
8
use PDO;
9
use PDOStatement;
10
11
class PdoClient implements ClientInterface
12
{
13
    const POSTFIX_FILTER = '_filter';
14
15
    /** @var PDO */
16
    private $pdo;
17
18 13
    public function __construct(PDO $pdo)
19
    {
20 13
        $this->pdo = $pdo;
21 13
    }
22
23
    /**
24
     * @throws BindingMicroOrmException
25
     * @throws ExecutionMicroOrmException
26
     */
27 3
    public function select(string $table, array $filter = [], array $orderBy = [], string $classFqcn = ''): array
28
    {
29 3
        $sqlSelect = "SELECT * FROM `$table`";
30 3
        $sqlFilter = $this->createSqlFilter($filter);
31 3
        $sqlOrderBy = $this->createSqlOrderBy($orderBy);
32
33 3
        $sql = $sqlSelect . ($sqlFilter ? ' ' . $sqlFilter : '') . ($sqlOrderBy ? ' ' . $sqlOrderBy : '');
34
35 3
        return $this->executeQuery($sql, $filter, $classFqcn);
36
    }
37
38
    /**
39
     * @param string $table
40
     * @param array  $data [$columnName => $value, ...]
41
     *
42
     * @return int The nbr of affected rows
43
     */
44 1
    public function insert(string $table, array $data): int
45
    {
46 1
        $sql = $this->createInsertSql($table, $data);
47
48 1
        return $this->executeCommand($sql, [], $data);
49
    }
50
51
    /**
52
     * @param string $table
53
     * @param array  $data [$columnName => $value, ...]
54
     *
55
     * @return int The nbr of affected rows
56
     */
57 1 View Code Duplication
    public function update(string $table, array $filter = [], array $data = []): int
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
58
    {
59 1
        $sqlUpdate = $this->createUpdateSql($table, $data);
60 1
        $sqlFilter = $this->createSqlFilter($filter);
61 1
        $sql = $sqlUpdate . ($sqlFilter ? ' ' . $sqlFilter : '');
62
63 1
        return $this->executeCommand($sql, $filter, $data);
64
    }
65
66
    /**
67
     * @return int The nbr of affected rows
68
     */
69 1 View Code Duplication
    public function delete(string $table, array $filter = []): int
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
70
    {
71 1
        $sqlDelete = "DELETE FROM `$table`";
72 1
        $sqlFilter = $this->createSqlFilter($filter);
73 1
        $sql = $sqlDelete . ($sqlFilter ? ' ' . $sqlFilter : '');
74
75 1
        return $this->executeCommand($sql, $filter);
76
    }
77
78
    /**
79
     * @throws BindingMicroOrmException
80
     * @throws ExecutionMicroOrmException
81
     *
82
     * @return array The result list
83
     */
84 2
    public function executeQuery(string $sql, array $filterList = [], string $classFqcn = null): array
85
    {
86 2
        return $this->fetchData($this->execute($sql, $filterList), $classFqcn);
87
    }
88
89
    /**
90
     * @throws BindingMicroOrmException
91
     * @throws ExecutionMicroOrmException
92
     *
93
     * @return int The nbr of affected rows
94
     */
95 4
    public function executeCommand(string $sql, array $filterList = [], array $dataList = []): int
96
    {
97 4
        return $this->execute($sql, $filterList, $dataList)->rowCount();
98
    }
99
100 1
    public function getLastInsertId(string $idName = null): string
101
    {
102 1
        return $this->pdo->lastInsertId($idName);
103
    }
104
105
    /**
106
     * @throws BindingMicroOrmException
107
     * @throws ExecutionMicroOrmException
108
     *
109
     * @return PDOStatement
110
     */
111 4
    private function execute(string $sql, array $filterList = [], array $dataList = []): PDOStatement
112
    {
113 4
        $stmt = $this->pdo->prepare($sql);
114
115 4
        $this->bindParameterList($stmt, $filterList, self::POSTFIX_FILTER);
116 2
        $this->bindParameterList($stmt, $dataList);
117
118 2
        $executed = $stmt->execute();
119 2
        if (false === $executed) {
120 1
            throw new ExecutionMicroOrmException(
121 1
                "Could not execute query: '$sql'"
122 1
                . " Error code: " . $stmt->errorCode()
123 1
                . " Error Info: " . json_encode($stmt->errorInfo())
124
            );
125
        }
126
127 1
        return $stmt;
128
    }
129
130 2
    private function fetchData(PDOStatement $stmt, string $classFqcn = null): array
131
    {
132 2
        if (! empty($classFqcn)) {
133 1
            return $stmt->fetchAll(PDO::FETCH_CLASS, $classFqcn);
134
        }
135
136 1
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
137
    }
138
139 5
    private function createSqlFilter(array $filterByColumnNames = []): string
140
    {
141 5
        if (empty($filterByColumnNames)) {
142 1
            return '';
143
        }
144
145 4
        $sqlFilter = [];
146 4
        foreach ($filterByColumnNames as $columnName => $value) {
147 4
            if (null === $value) {
148 3
                $sqlFilter[] = "`$columnName` IS :$columnName" . self::POSTFIX_FILTER;
149
            } else {
150 4
                $sqlFilter[] = "`$columnName`=:$columnName" . self::POSTFIX_FILTER;
151
            }
152
        }
153 4
        $sqlFilter = 'WHERE ' . implode(' AND ', $sqlFilter);
154
155 4
        return $sqlFilter;
156
    }
157
158 3
    private function createSqlOrderBy(array $orderBy = []): string
159
    {
160 3
        if (empty($orderBy)) {
161 1
            return '';
162
        }
163
164 2
        $orderByItems = [];
165 2
        foreach ($orderBy as $column => $direction) {
166 2
            $orderByItems[] = $column . ' ' . $direction;
167
        }
168
169 2
        return 'ORDER BY ' . implode(', ', $orderByItems);
170
    }
171
172 1
    private function createInsertSql(string $tableName, array $dataList): string
173
    {
174 1
        $columnNamesArray = array_keys($dataList);
175 1
        $columnNamesList = "`" . implode("`, `", $columnNamesArray) . "`";
176 1
        $columnPlaceholdersList = ":" . implode(", :", $columnNamesArray);
177
178 1
        return "INSERT INTO `$tableName` ($columnNamesList) VALUES ($columnPlaceholdersList)";
179
    }
180
181 1
    private function createUpdateSql(string $tableName, array $parameterList): string
182
    {
183 1
        $setColumnsList = [];
184 1
        foreach ($parameterList as $columnName => $columnValue) {
185 1
            $setColumnsList[] = '`' . $columnName . '`=:' . $columnName;
186
        }
187 1
        $setColumnsList = implode(', ', $setColumnsList);
188
189 1
        return "UPDATE `$tableName` SET $setColumnsList";
190
    }
191
192
    /**
193
     * @param mixed $value
194
     *
195
     * @throws TypeResolutionMicroOrmException
196
     */
197 2
    private function resolvePdoType($value): int
198
    {
199 2
        $type = gettype($value);
200
        switch ($type) {
201 2
            case 'boolean':
202 1
                $pdoType = PDO::PARAM_BOOL;
203 1
                break;
204 2
            case 'string' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
205 2
            case 'double':
206
                // float
207 1
                $pdoType = PDO::PARAM_STR;
208 1
                break;
209 2
            case 'integer':
210 1
                $pdoType = PDO::PARAM_INT;
211 1
                break;
212 2
            case 'NULL':
213 1
                $pdoType = PDO::PARAM_NULL;
214 1
                break;
215 1
            case 'object':
216 1
                $class = get_class($value);
217 1
                throw new TypeResolutionMicroOrmException("Invalid type '$class' for query filter.");
218
            default:
219
                throw new TypeResolutionMicroOrmException("Invalid type '$type' for query filter.");
220
        }
221
222 1
        return $pdoType;
223
    }
224
225
    /**
226
     * @throws BindingMicroOrmException
227
     */
228 8
    private function bindParameterList(PDOStatement $stmt, array $parameterList, string $postfix = '')
229
    {
230 8
        foreach ($parameterList as $name => $value) {
231
232 7
            $pdoType = $this->resolvePdoType($value);
233 7
            $bound = $stmt->bindValue(
234 7
                ':' . $name . $postfix,
235 7
                $pdoType === PDO::PARAM_STR ? strval($value) : $value,
236
                $pdoType
237
            );
238
239 7
            if (false === $bound) {
240 1
                throw new BindingMicroOrmException(
241 7
                    "Could not bind value: " . json_encode(['name' => $name, 'value' => $value, 'type' => $pdoType])
242
                );
243
            }
244
        }
245 7
    }
246
}
247