Passed
Pull Request — master (#26)
by
unknown
01:52
created

DataLayer::getColumns()   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
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 5
rs 10
1
<?php
2
3
namespace CoffeeCode\DataLayer;
4
5
use Exception;
6
use PDO;
7
use PDOException;
8
use stdClass;
9
10
/**
11
 * Class DataLayer
12
 * @package CoffeeCode\DataLayer
13
 */
14
abstract class DataLayer
15
{
16
    use CrudTrait;
17
18
    /** @var string $entity database table */
19
    private $entity;
20
21
    /** @var string $primary table primary key field */
22
    private $primary;
23
24
    /** @var array $required table required fields */
25
    private $required;
26
27
    /** @var string $timestamps control created and updated at */
28
    private $timestamps;
29
30
    /** @var string */
31
    protected $statement;
32
33
    /** @var string */
34
    protected $params;
35
36
    /** @var string */
37
    protected $group;
38
39
    /** @var string */
40
    protected $order;
41
42
    /** @var int */
43
    protected $limit;
44
45
    /** @var int */
46
    protected $offset;
47
48
    /** @var \PDOException|null */
49
    protected $fail;
50
51
    /** @var object|null */
52
    protected $data;
53
54
    /** @var array|null */
55
    protected $columns;
56
57
    /**
58
     * DataLayer constructor.
59
     * @param string $entity
60
     * @param array $required
61
     * @param string $primary
62
     * @param bool $timestamps
63
     */
64
    public function __construct(string $entity, array $required, string $primary = 'id', bool $timestamps = true)
65
    {
66
        $this->entity = $entity;
67
        $this->primary = $primary;
68
        $this->required = $required;
69
        $this->timestamps = $timestamps;
0 ignored issues
show
Documentation Bug introduced by
The property $timestamps was declared of type string, but $timestamps is of type boolean. 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...
70
        $this->columns = $this->getColumns();
71
    }
72
73
    /**
74
     * @param $name
75
     * @param $value
76
     */
77
    public function __set($name, $value)
78
    {
79
        if (empty($this->data)) {
80
            $this->data = new stdClass();
81
        }
82
83
        $this->data->$name = $value;
84
    }
85
86
    /**
87
     * @param $name
88
     * @return bool
89
     */
90
    public function __isset($name)
91
    {
92
        return isset($this->data->$name);
93
    }
94
95
    /**
96
     * @param $name
97
     * @return string|null
98
     */
99
    public function __get($name)
100
    {
101
        $method = $this->toCamelCase($name);
102
        if (method_exists($this, $method)) {
103
            return $this->$method();
104
        }
105
106
        if (method_exists($this, $name)) {
107
            return $this->$name();
108
        }
109
110
        return ($this->data->$name ?? null);
111
    }
112
113
     /*
114
     * @return array|null
115
     */
116
    private function getColumns() : ?array
117
    {
118
        $stmt = Connect::getInstance()->prepare( "DESCRIBE {$this->entity}");
119
        $stmt->execute($this->params);
0 ignored issues
show
Bug introduced by
$this->params of type string is incompatible with the type array expected by parameter $input_parameters of PDOStatement::execute(). ( Ignorable by Annotation )

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

119
        $stmt->execute(/** @scrutinizer ignore-type */ $this->params);
Loading history...
120
        return $stmt->fetchAll(PDO::FETCH_COLUMN);
121
    }
122
    
123
124
    /**
125
     * @return object|null
126
     */
127
    public function data(): ?object
128
    {
129
        return $this->data;
130
    }
131
132
    /**
133
     * @return PDOException|Exception|null
134
     */
135
    public function fail()
136
    {
137
        return $this->fail;
138
    }
139
140
    /**
141
     * @param string|null $terms
142
     * @param string|null $params
143
     * @param string $columns
144
     * @return DataLayer
145
     */
146
    public function find(?string $terms = null, ?string $params = null, string $columns = "*"): DataLayer
147
    {
148
        if ($terms) {
149
            $this->statement = "SELECT {$columns} FROM {$this->entity} WHERE {$terms}";
150
            parse_str($params, $this->params);
0 ignored issues
show
Bug introduced by
$this->params of type string is incompatible with the type array|null expected by parameter $arr of parse_str(). ( Ignorable by Annotation )

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

150
            parse_str($params, /** @scrutinizer ignore-type */ $this->params);
Loading history...
151
            return $this;
152
        }
153
154
        $this->statement = "SELECT {$columns} FROM {$this->entity}";
155
        return $this;
156
    }
157
158
    /**
159
     * @param int $id
160
     * @param string $columns
161
     * @return DataLayer|null
162
     */
163
    public function findById(int $id, string $columns = "*"): ?DataLayer
164
    {
165
        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...
166
    }
167
168
    /**
169
     * @param string $column
170
     * @return DataLayer|null
171
     */
172
    public function group(string $column): ?DataLayer
173
    {
174
        $this->group = " GROUP BY {$column}";
175
        return $this;
176
    }
177
178
    /**
179
     * @param string $columnOrder
180
     * @return DataLayer|null
181
     */
182
    public function order(string $columnOrder): ?DataLayer
183
    {
184
        $this->order = " ORDER BY {$columnOrder}";
185
        return $this;
186
    }
187
188
    /**
189
     * @param int $limit
190
     * @return DataLayer|null
191
     */
192
    public function limit(int $limit): ?DataLayer
193
    {
194
        $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...
195
        return $this;
196
    }
197
198
    /**
199
     * @param int $offset
200
     * @return DataLayer|null
201
     */
202
    public function offset(int $offset): ?DataLayer
203
    {
204
        $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...
205
        return $this;
206
    }
207
208
    /**
209
     * @param bool $all
210
     * @return array|mixed|null
211
     */
212
    public function fetch(bool $all = false)
213
    {
214
        try {
215
            $stmt = Connect::getInstance()->prepare($this->statement . $this->group . $this->order . $this->limit . $this->offset);
216
            $stmt->execute($this->params);
0 ignored issues
show
Bug introduced by
$this->params of type string is incompatible with the type array expected by parameter $input_parameters of PDOStatement::execute(). ( Ignorable by Annotation )

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

216
            $stmt->execute(/** @scrutinizer ignore-type */ $this->params);
Loading history...
217
218
            if (!$stmt->rowCount()) {
219
                return null;
220
            }
221
222
            if ($all) {
223
                return $stmt->fetchAll(PDO::FETCH_CLASS, static::class);
224
            }
225
226
            return $stmt->fetchObject(static::class);
227
        } catch (PDOException $exception) {
228
            $this->fail = $exception;
229
            return null;
230
        }
231
    }
232
233
    /**
234
     * @return int
235
     */
236
    public function count(): int
237
    {
238
        $stmt = Connect::getInstance()->prepare($this->statement);
239
        $stmt->execute($this->params);
0 ignored issues
show
Bug introduced by
$this->params of type string is incompatible with the type array expected by parameter $input_parameters of PDOStatement::execute(). ( Ignorable by Annotation )

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

239
        $stmt->execute(/** @scrutinizer ignore-type */ $this->params);
Loading history...
240
        return $stmt->rowCount();
241
    }
242
243
    /**
244
     * @return bool
245
     */
246
    public function save(): bool
247
    {
248
        $primary = $this->primary;
249
        $id = null;
250
251
        try {
252
            if (!$this->required()) {
253
                throw new Exception("Preencha os campos necessários");
254
            }
255
256
            /** Update */
257
            if (!empty($this->data->$primary)) {
258
                $id = $this->data->$primary;
259
                $this->update($this->safe(), "{$this->primary} = :id", "id={$id}");
260
            }
261
262
            /** Create */
263
            if (empty($this->data->$primary)) {
264
                $id = $this->create($this->safe());
265
            }
266
267
            if (!$id) {
268
                return false;
269
            }
270
271
            $this->data = $this->findById($id)->data();
272
            return true;
273
        } catch (Exception $exception) {
274
            $this->fail = $exception;
0 ignored issues
show
Documentation Bug introduced by
$exception is of type Exception, but the property $fail was declared to be of type PDOException|null. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
275
            return false;
276
        }
277
    }
278
279
    /**
280
     * @return bool
281
     */
282
    public function destroy(): bool
283
    {
284
        $primary = $this->primary;
285
        $id = $this->data->$primary;
286
287
        if (empty($id)) {
288
            return false;
289
        }
290
291
        return $this->delete("{$this->primary} = :id", "id={$id}");
292
    }
293
294
    /**
295
     * @return bool
296
     */
297
    protected function required(): bool
298
    {
299
        $data = (array)$this->data();
300
        foreach ($this->required as $field) {
301
            if (empty($data[$field])) {
302
                return false;
303
            }
304
        }
305
        return true;
306
    }
307
308
    /**
309
     * @return array|null
310
     */
311
    protected function safe(): ?array
312
    {
313
        $safe = (array)$this->data;
314
        unset($safe[$this->primary]);
315
        return $safe;
316
    }
317
318
319
    /**
320
     * @param string $string
321
     * @return string
322
     */
323
    protected function toCamelCase(string $string): string
324
    {
325
        $camelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
326
        $camelCase[0] = strtolower($camelCase[0]);
327
        return $camelCase;
328
    }
329
}
330