Passed
Pull Request — master (#21)
by
unknown
02:00
created

DataLayer   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 291
Duplicated Lines 0 %

Importance

Changes 9
Bugs 0 Features 0
Metric Value
wmc 32
eloc 87
c 9
b 0
f 0
dl 0
loc 291
rs 9.84

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __get() 0 3 1
A data() 0 3 1
A __isset() 0 3 1
A __construct() 0 6 1
A __set() 0 7 2
A count() 0 5 1
A offset() 0 4 1
A limit() 0 4 1
A find() 0 10 2
A setData() 0 3 1
A required() 0 9 3
A save() 0 30 6
A order() 0 4 1
A fail() 0 3 1
A destroy() 0 11 2
A findById() 0 4 1
A group() 0 4 1
A safe() 0 6 1
A fetch() 0 18 4
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
    /**
55
     * DataLayer constructor.
56
     * @param string $entity
57
     * @param array $required
58
     * @param string $primary
59
     * @param bool $timestamps
60
     */
61
    public function __construct(string $entity, array $required, string $primary = 'id', bool $timestamps = true)
62
    {
63
        $this->entity = $entity;
64
        $this->primary = $primary;
65
        $this->required = $required;
66
        $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...
67
    }
68
69
    /**
70
     * @param $name
71
     * @param $value
72
     */
73
    public function __set($name, $value)
74
    {
75
        if (empty($this->data)) {
76
            $this->data = new stdClass();
77
        }
78
79
        $this->data->$name = $value;
80
    }
81
82
    /**
83
     * @param $name
84
     * @return bool
85
     */
86
    public function __isset($name)
87
    {
88
        return isset($this->data->$name);
89
    }
90
91
    /**
92
     * @param $name
93
     * @return string|null
94
     */
95
    public function __get($name)
96
    {
97
        return ($this->data->$name ?? null);
98
    }
99
100
    /**
101
     * @return object|null
102
     */
103
    public function data(): ?object
104
    {
105
        return $this->data;
106
    }
107
    
108
    /**
109
     * @param array|null $data
110
     *
111
     * @return object|null
112
     */
113
    public function setData(?array $data): ?object
114
    {
115
        return $this->data = (object)$data;
116
    }
117
118
    /**
119
     * @return PDOException|Exception|null
120
     */
121
    public function fail()
122
    {
123
        return $this->fail;
124
    }
125
126
    /**
127
     * @param string|null $terms
128
     * @param string|null $params
129
     * @param string $columns
130
     * @return DataLayer
131
     */
132
    public function find(?string $terms = null, ?string $params = null, string $columns = "*"): DataLayer
133
    {
134
        if ($terms) {
135
            $this->statement = "SELECT {$columns} FROM {$this->entity} WHERE {$terms}";
136
            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

136
            parse_str($params, /** @scrutinizer ignore-type */ $this->params);
Loading history...
137
            return $this;
138
        }
139
140
        $this->statement = "SELECT {$columns} FROM {$this->entity}";
141
        return $this;
142
    }
143
144
    /**
145
     * @param int $id
146
     * @param string $columns
147
     * @return DataLayer|null
148
     */
149
    public function findById(int $id, string $columns = "*"): ?DataLayer
150
    {
151
        $find = $this->find($this->primary . " = :id", "id={$id}", $columns);
152
        return $find->fetch();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $find->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...
153
    }
154
155
    /**
156
     * @param string $column
157
     * @return DataLayer|null
158
     */
159
    public function group(string $column): ?DataLayer
160
    {
161
        $this->group = " GROUP BY {$column}";
162
        return $this;
163
    }
164
165
    /**
166
     * @param string $columnOrder
167
     * @return DataLayer|null
168
     */
169
    public function order(string $columnOrder): ?DataLayer
170
    {
171
        $this->order = " ORDER BY {$columnOrder}";
172
        return $this;
173
    }
174
175
    /**
176
     * @param int $limit
177
     * @return DataLayer|null
178
     */
179
    public function limit(int $limit): ?DataLayer
180
    {
181
        $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...
182
        return $this;
183
    }
184
185
    /**
186
     * @param int $offset
187
     * @return DataLayer|null
188
     */
189
    public function offset(int $offset): ?DataLayer
190
    {
191
        $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...
192
        return $this;
193
    }
194
195
    /**
196
     * @param bool $all
197
     * @return array|mixed|null
198
     */
199
    public function fetch(bool $all = false)
200
    {
201
        try {
202
            $stmt = Connect::getInstance()->prepare($this->statement . $this->group . $this->order . $this->limit . $this->offset);
203
            $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

203
            $stmt->execute(/** @scrutinizer ignore-type */ $this->params);
Loading history...
204
205
            if (!$stmt->rowCount()) {
206
                return null;
207
            }
208
209
            if ($all) {
210
                return $stmt->fetchAll(PDO::FETCH_CLASS, static::class);
211
            }
212
213
            return $stmt->fetchObject(static::class);
214
        } catch (PDOException $exception) {
215
            $this->fail = $exception;
216
            return null;
217
        }
218
    }
219
220
    /**
221
     * @return int
222
     */
223
    public function count(): int
224
    {
225
        $stmt = Connect::getInstance()->prepare($this->statement);
226
        $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

226
        $stmt->execute(/** @scrutinizer ignore-type */ $this->params);
Loading history...
227
        return $stmt->rowCount();
228
    }
229
230
    /**
231
     * @return bool
232
     */
233
    public function save(): bool
234
    {
235
        $primary = $this->primary;
236
        $id = null;
237
238
        try {
239
            if (!$this->required()) {
240
                throw new Exception("Preencha os campos necessários");
241
            }
242
243
            /** Update */
244
            if (!empty($this->data->$primary)) {
245
                $id = $this->data->$primary;
246
                $this->update($this->safe(), $this->primary . " = :id", "id={$id}");
247
            }
248
249
            /** Create */
250
            if (empty($this->data->$primary)) {
251
                $id = $this->create($this->safe());
252
            }
253
254
            if (!$id) {
255
                return false;
256
            }
257
258
            $this->data = $this->findById($id)->data();
259
            return true;
260
        } catch (Exception $exception) {
261
            $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...
262
            return false;
263
        }
264
    }
265
266
    /**
267
     * @return bool
268
     */
269
    public function destroy(): bool
270
    {
271
        $primary = $this->primary;
272
        $id = $this->data->$primary;
273
274
        if (empty($id)) {
275
            return false;
276
        }
277
278
        $destroy = $this->delete($this->primary . " = :id", "id={$id}");
279
        return $destroy;
280
    }
281
282
    /**
283
     * @return bool
284
     */
285
    protected function required(): bool
286
    {
287
        $data = (array)$this->data();
288
        foreach ($this->required as $field) {
289
            if (empty($data[$field])) {
290
                return false;
291
            }
292
        }
293
        return true;
294
    }
295
296
    /**
297
     * @return array|null
298
     */
299
    protected function safe(): ?array
300
    {
301
        $safe = (array)$this->data;
302
        unset($safe[$this->primary]);
303
304
        return $safe;
305
    }
306
}
307