DataLayer::fetch()   A
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 12
c 3
b 0
f 0
dl 0
loc 20
rs 9.8666
cc 4
nc 8
nop 1
1
<?php
2
3
namespace CoffeeCode\DataLayer;
4
5
use PDO;
6
use PDOException;
7
use stdClass;
8
9
/**
10
 * Class DataLayer
11
 * @package CoffeeCode\DataLayer
12
 */
13
abstract class DataLayer
14
{
15
    use CrudTrait;
16
17
    /** @var string $entity database table */
18
    private string $entity;
19
20
    /** @var string $primary table primary key field */
21
    private string $primary;
22
23
    /** @var array $required table required fields */
24
    private array $required;
25
26
    /** @var bool $timestamps control created and updated at */
27
    private bool $timestamps;
28
29
    /** @var array|null */
30
    private ?array $database = null;
31
32
    /** @var string|null */
33
    protected ?string $statement = null;
34
35
    /** @var array|null */
36
    protected ?array $params = null;
37
38
    /** @var string */
39
    protected ?string $group = null;
40
41
    /** @var string|null */
42
    protected ?string $order = null;
43
44
    /** @var int */
45
    protected ?string $limit = null;
46
47
    /** @var int */
48
    protected ?int $offset = null;
49
50
    /** @var PDOException|null */
51
    protected ?PDOException $fail = null;
52
53
    /** @var object|null */
54
    protected ?object $data = null;
55
56
    /**
57
     * DataLayer constructor.
58
     * @param string $entity
59
     * @param array $required
60
     * @param string $primary
61
     * @param bool $timestamps
62
     */
63
    public function __construct(
64
        string $entity,
65
        array $required,
66
        string $primary = 'id',
67
        bool $timestamps = true,
68
        array $database = null
69
    ) {
70
        $this->entity = $entity;
71
        $this->primary = $primary;
72
        $this->required = $required;
73
        $this->timestamps = $timestamps;
74
        $this->database = $database;
75
    }
76
77
    /**
78
     * @param $name
79
     * @param $value
80
     */
81
    public function __set($name, $value)
82
    {
83
        if (empty($this->data)) {
84
            $this->data = new stdClass();
85
        }
86
87
        $this->data->$name = $value;
88
    }
89
90
    /**
91
     * @param $name
92
     * @return bool
93
     */
94
    public function __isset($name)
95
    {
96
        return isset($this->data->$name);
97
    }
98
99
    /**
100
     * @param $name
101
     * @return string|null
102
     */
103
    public function __get($name)
104
    {
105
        $method = $this->toCamelCase($name);
106
        if (method_exists($this, $method)) {
107
            return $this->$method();
108
        }
109
110
        if (method_exists($this, $name)) {
111
            return $this->$name();
112
        }
113
114
        return ($this->data->$name ?? null);
115
    }
116
117
    /**
118
     * @param int $mode
119
     * @return array
120
     */
121
    public function columns($mode = PDO::FETCH_OBJ): ?array
122
    {
123
        $stmt = Connect::getInstance($this->database)->prepare("DESCRIBE {$this->entity}");
124
        $stmt->execute($this->params);
125
        return $stmt->fetchAll($mode);
126
    }
127
128
129
    /**
130
     * @return object|null
131
     */
132
    public function data(): ?object
133
    {
134
        return $this->data;
135
    }
136
137
    /**
138
     * @return PDOException|null
139
     */
140
    public function fail(): ?PDOException
141
    {
142
        return $this->fail;
143
    }
144
145
    /**
146
     * @param string|null $terms
147
     * @param string|null $params
148
     * @param string $columns
149
     * @return DataLayer
150
     */
151
    public function find(?string $terms = null, ?string $params = null, string $columns = "*"): DataLayer
152
    {
153
        if ($terms) {
154
            $this->statement = "SELECT {$columns} FROM {$this->entity} WHERE {$terms}";
155
            parse_str($params, $this->params);
0 ignored issues
show
Bug introduced by
It seems like $params can also be of type null; however, parameter $string of parse_str() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

155
            parse_str(/** @scrutinizer ignore-type */ $params, $this->params);
Loading history...
156
            return $this;
157
        }
158
159
        $this->statement = "SELECT {$columns} FROM {$this->entity}";
160
        return $this;
161
    }
162
163
    /**
164
     * @param int $id
165
     * @param string $columns
166
     * @return DataLayer|null
167
     */
168
    public function findById(int $id, string $columns = "*"): ?DataLayer
169
    {
170
        return $this->find("{$this->primary} = :id", "id={$id}", $columns)->fetch();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->find($this...$id, $columns)->fetch() could return the type array which is incompatible with the type-hinted return CoffeeCode\DataLayer\DataLayer|null. Consider adding an additional type-check to rule them out.
Loading history...
171
    }
172
173
    /**
174
     * @param string $column
175
     * @return DataLayer|null
176
     */
177
    public function group(string $column): ?DataLayer
178
    {
179
        $this->group = " GROUP BY {$column}";
180
        return $this;
181
    }
182
183
    /**
184
     * @param string $columnOrder
185
     * @return DataLayer|null
186
     */
187
    public function order(string $columnOrder): ?DataLayer
188
    {
189
        $this->order = " ORDER BY {$columnOrder}";
190
        return $this;
191
    }
192
193
    /**
194
     * @param int $limit
195
     * @return DataLayer|null
196
     */
197
    public function limit(int $limit): ?DataLayer
198
    {
199
        $this->limit = " LIMIT {$limit}";
0 ignored issues
show
Documentation Bug introduced by
The property $limit was declared of type integer, but ' LIMIT '.$limit is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
200
        return $this;
201
    }
202
203
    /**
204
     * @param int $offset
205
     * @return DataLayer|null
206
     */
207
    public function offset(int $offset): ?DataLayer
208
    {
209
        $this->offset = " OFFSET {$offset}";
0 ignored issues
show
Documentation Bug introduced by
The property $offset was declared of type integer, but ' OFFSET '.$offset is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
210
        return $this;
211
    }
212
213
    /**
214
     * @param bool $all
215
     * @return array|mixed|null
216
     */
217
    public function fetch(bool $all = false)
218
    {
219
        try {
220
            $stmt = Connect::getInstance($this->database)->prepare(
221
                $this->statement . $this->group . $this->order . $this->limit . $this->offset
222
            );
223
            $stmt->execute($this->params);
224
225
            if (!$stmt->rowCount()) {
226
                return null;
227
            }
228
229
            if ($all) {
230
                return $stmt->fetchAll(PDO::FETCH_CLASS, static::class);
231
            }
232
233
            return $stmt->fetchObject(static::class);
234
        } catch (PDOException $exception) {
235
            $this->fail = $exception;
236
            return null;
237
        }
238
    }
239
240
    /**
241
     * @return int
242
     */
243
    public function count(): int
244
    {
245
        $stmt = Connect::getInstance($this->database)->prepare($this->statement);
246
        $stmt->execute($this->params);
247
        return $stmt->rowCount();
248
    }
249
250
    /**
251
     * @return bool
252
     */
253
    public function save(): bool
254
    {
255
        $primary = $this->primary;
256
        $id = null;
257
        $save = null;
258
259
        try {
260
            if (!$this->required()) {
261
                throw new PDOException("Preencha os campos necessários");
262
            }
263
264
            /** Update */
265
            if (!empty($this->data->$primary)) {
266
                $id = $this->data->$primary;
267
                $save = $this->update($this->safe(), "{$this->primary} = :id", "id={$id}");
268
            }
269
270
            /** Create */
271
            if (empty($this->data->$primary)) {
272
                $id = $this->create($this->safe());
273
                $save = $id;
274
            }
275
276
            if ($save === null) {
277
                return false;
278
            }
279
280
            $this->data = $this->findById($id)->data();
281
            return true;
282
        } catch (PDOException $exception) {
283
            $this->fail = $exception;
284
            return false;
285
        }
286
    }
287
288
    /**
289
     * @return bool
290
     */
291
    public function destroy(): bool
292
    {
293
        $primary = $this->primary;
294
        $id = $this->data->$primary;
295
296
        if (empty($id)) {
297
            return false;
298
        }
299
300
        return $this->delete("{$this->primary} = :id", "id={$id}");
301
    }
302
303
    /**
304
     * @return bool
305
     */
306
    protected function required(): bool
307
    {
308
        $data = (array)$this->data();
309
        foreach ($this->required as $field) {
310
            if (empty($data[$field])) {
311
                if (!is_int($data[$field])) {
312
                    return false;
313
                }
314
            }
315
        }
316
        return true;
317
    }
318
319
    /**
320
     * @return array|null
321
     */
322
    protected function safe(): ?array
323
    {
324
        $safe = (array)$this->data;
325
        unset($safe[$this->primary]);
326
        return $safe;
327
    }
328
329
330
    /**
331
     * @param string $string
332
     * @return string
333
     */
334
    protected function toCamelCase(string $string): string
335
    {
336
        $camelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
337
        $camelCase[0] = strtolower($camelCase[0]);
338
        return $camelCase;
339
    }
340
}
341