Passed
Push — master ( 718fb6...23a26d )
by Melech
04:05
created

PdoStatement::mapResultsToEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Orm\Statement;
15
16
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use PDO;
18
use PDOStatement as Statement;
19
use Valkyrja\Orm\Data\Value;
20
use Valkyrja\Orm\Entity\Contract\Entity;
21
use Valkyrja\Orm\Exception\RuntimeException;
22
use Valkyrja\Orm\QueryBuilder\Contract\QueryBuilder;
23
use Valkyrja\Orm\Statement\Contract\Statement as Contract;
24
25
use function is_array;
26
use function is_bool;
27
use function is_int;
28
use function is_string;
29
30
/**
31
 * Class PdoStatement.
32
 *
33
 * @author Melech Mizrachi
34
 */
35
class PdoStatement implements Contract
36
{
37
    /**
38
     * PdoStatement constructor.
39
     *
40
     * @param Statement $statement The pdo statement
41
     */
42
    public function __construct(
43
        protected Statement $statement
44
    ) {
45
    }
46
47
    /**
48
     * @inheritDoc
49
     */
50
    #[Override]
51
    public function bindValue(Value $value): bool
52
    {
53
        if ($value->value instanceof QueryBuilder) {
54
            return true;
55
        }
56
57
        if (is_array($value->value)) {
58
            $ret = false;
59
60
            foreach ($value->value as $key => $item) {
61
                $ret = $this->statement->bindValue(
62
                    param: ":$value->name$key",
63
                    value: $item,
64
                    type: $this->getBindValueType($item)
65
                );
66
            }
67
68
            return $ret;
69
        }
70
71
        return $this->statement->bindValue(
72
            param: ":$value->name",
73
            value: $value->value,
74
            type: $this->getBindValueType($value->value)
75
        );
76
    }
77
78
    /**
79
     * @inheritDoc
80
     */
81
    #[Override]
82
    public function execute(): bool
83
    {
84
        return $this->statement->execute();
85
    }
86
87
    /**
88
     * @inheritDoc
89
     */
90
    #[Override]
91
    public function getColumnMeta(int $columnNumber): array
92
    {
93
        /** @var array<string, mixed>|false $columnMeta */
94
        $columnMeta = $this->statement->getColumnMeta($columnNumber);
95
96
        if ($columnMeta === false) {
97
            throw new RuntimeException(
98
                $this->errorMessage()
99
                ?? "Error occurred when getting column meta for column number $columnNumber"
100
            );
101
        }
102
103
        return $columnMeta;
104
    }
105
106
    /**
107
     * @inheritDoc
108
     *
109
     * @template T of Entity
110
     *
111
     * @param class-string<T>|null $entity The entity class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>|null.
Loading history...
112
     *
113
     * @return ($entity is class-string<T> ? T : array<string, mixed>)
0 ignored issues
show
Documentation Bug introduced by
The doc comment ($entity at position 1 could not be parsed: Unknown type name '$entity' at position 1 in ($entity.
Loading history...
114
     */
115
    #[Override]
116
    public function fetch(string|null $entity = null): Entity|array
117
    {
118
        /** @var array<string, mixed>|false $fetch */
119
        $fetch = $this->statement->fetch(PDO::FETCH_ASSOC);
120
121
        if (! is_array($fetch)) {
122
            throw new RuntimeException($this->errorMessage() ?? 'Error occurred when fetching');
123
        }
124
125
        if ($entity !== null) {
126
            /** @var T $entityClass */
127
            $entityClass = $entity::fromArray($fetch);
128
129
            return $entityClass;
130
        }
131
132
        return $fetch;
133
    }
134
135
    /**
136
     * @inheritDoc
137
     */
138
    #[Override]
139
    public function fetchColumn(int $columnNumber = 0): mixed
140
    {
141
        return $this->statement->fetchColumn($columnNumber);
142
    }
143
144
    /**
145
     * @inheritDoc
146
     *
147
     * @template T of Entity
148
     *
149
     * @param class-string<T>|null $entity The entity class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>|null.
Loading history...
150
     *
151
     * @return ($entity is class-string<T> ? T[] : array<string, mixed>[])
0 ignored issues
show
Documentation Bug introduced by
The doc comment ($entity at position 1 could not be parsed: Unknown type name '$entity' at position 1 in ($entity.
Loading history...
152
     */
153
    #[Override]
154
    public function fetchAll(string|null $entity = null): array
155
    {
156
        /** @var array<string, mixed>[] $fetch */
157
        $fetch = $this->statement->fetchAll(PDO::FETCH_ASSOC);
158
159
        if ($entity !== null) {
160
            /** @var T[] $entities */
161
            $entities = $this->mapResultsToEntity($entity, $fetch);
162
163
            return $entities;
164
        }
165
166
        return $fetch;
167
    }
168
169
    /**
170
     * @inheritDoc
171
     */
172
    #[Override]
173
    public function getCount(): int
174
    {
175
        $results = $this->statement->fetchAll();
176
177
        /** @var array<string, int|string|mixed>|null $firstResults */
178
        $firstResults = $results[0] ?? null;
179
180
        if ($firstResults === null) {
181
            return 0;
182
        }
183
184
        /** @var mixed $count */
185
        $count = $firstResults['COUNT(*)']
186
            ?? $firstResults['count']
187
            ?? 0;
188
189
        if (is_int($count)) {
190
            return $count;
191
        }
192
193
        if (is_string($count)) {
194
            return (int) $count;
195
        }
196
197
        throw new RuntimeException('Unsupported count results');
198
    }
199
200
    /**
201
     * @inheritDoc
202
     */
203
    #[Override]
204
    public function rowCount(): int
205
    {
206
        return $this->statement->rowCount();
207
    }
208
209
    /**
210
     * @inheritDoc
211
     */
212
    #[Override]
213
    public function columnCount(): int
214
    {
215
        return $this->statement->columnCount();
216
    }
217
218
    /**
219
     * @inheritDoc
220
     */
221
    #[Override]
222
    public function errorCode(): string
223
    {
224
        return $this->statement->errorInfo()[0] ?? '00000';
225
    }
226
227
    /**
228
     * @inheritDoc
229
     */
230
    #[Override]
231
    public function errorMessage(): string|null
232
    {
233
        return $this->statement->errorInfo()[2] ?? null;
234
    }
235
236
    /**
237
     * Get value type to bind with.
238
     *
239
     * @param mixed $value
240
     *
241
     * @return int
242
     */
243
    protected function getBindValueType(mixed $value): int
244
    {
245
        return match (true) {
246
            is_int($value)  => PDO::PARAM_INT,
247
            is_bool($value) => PDO::PARAM_BOOL,
248
            $value === null => PDO::PARAM_NULL,
249
            default         => PDO::PARAM_STR,
250
        };
251
    }
252
253
    /**
254
     * @template T of Entity
255
     *
256
     * @param class-string<T>        $entity  The entity class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
257
     * @param array<string, mixed>[] $results The results
258
     *
259
     * @return T[]
260
     */
261
    protected function mapResultsToEntity(string $entity, array $results): array
262
    {
263
        return array_map(
264
            static fn (array $data): Entity => $entity::fromArray($data),
265
            $results
266
        );
267
    }
268
}
269