Passed
Pull Request — master (#70)
by Kevin
05:50
created

ScalarBeanPropertyDescriptor::canBeSerialized()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace TheCodingMachine\TDBM\Utils;
4
5
use Doctrine\DBAL\Schema\Column;
6
use Doctrine\DBAL\Schema\Table;
7
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
8
use Doctrine\DBAL\Types\DateTimeImmutableType;
9
use Doctrine\DBAL\Types\DateTimeType;
10
use Ramsey\Uuid\Uuid;
11
use TheCodingMachine\TDBM\TDBMException;
12
use TheCodingMachine\TDBM\Utils\Annotation\Annotation;
13
use TheCodingMachine\TDBM\Utils\Annotation\AnnotationParser;
14
use TheCodingMachine\TDBM\Utils\Annotation\Annotations;
15
16
/**
17
 * This class represent a property in a bean (a property has a getter, a setter, etc...).
18
 */
19
class ScalarBeanPropertyDescriptor extends AbstractBeanPropertyDescriptor
20
{
21
    /**
22
     * @var Column
23
     */
24
    private $column;
25
26
    /**
27
     * @var Annotations
28
     */
29
    private $annotations;
30
31
    /**
32
     * ScalarBeanPropertyDescriptor constructor.
33
     * @param Table $table
34
     * @param Column $column
35
     * @param NamingStrategyInterface $namingStrategy
36
     */
37
    public function __construct(Table $table, Column $column, NamingStrategyInterface $namingStrategy)
38
    {
39
        parent::__construct($table, $namingStrategy);
40
        $this->table = $table;
41
        $this->column = $column;
42
    }
43
44
    /**
45
     * Returns the foreign-key the column is part of, if any. null otherwise.
46
     *
47
     * @return ForeignKeyConstraint|null
48
     */
49
    public function getForeignKey()
50
    {
51
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type null|Doctrine\DBAL\Schema\ForeignKeyConstraint.
Loading history...
52
    }
53
54
    /**
55
     * Returns the param annotation for this property (useful for constructor).
56
     *
57
     * @return string
58
     */
59
    public function getParamAnnotation()
60
    {
61
        $paramType = $this->getPhpType();
62
63
        $str = '     * @param %s %s';
64
65
        return sprintf($str, $paramType, $this->getVariableName());
66
    }
67
68
    /**
69
     * Returns the name of the class linked to this property or null if this is not a foreign key.
70
     *
71
     * @return null|string
72
     */
73
    public function getClassName(): ?string
74
    {
75
        return null;
76
    }
77
78
    /**
79
     * Returns the PHP type for the property (it can be a scalar like int, bool, or class names, like \DateTimeInterface, App\Bean\User....)
80
     *
81
     * @return string
82
     */
83
    public function getPhpType(): string
84
    {
85
        $type = $this->column->getType();
86
        return TDBMDaoGenerator::dbalTypeToPhpType($type);
87
    }
88
89
    /**
90
     * Returns the PHP type for the property (it can be a scalar like int, bool, or class names, like \DateTimeInterface, App\Bean\User....)
91
     *
92
     * @return string
93
     */
94
    public function canBeSerialized(): string
95
    {
96
        $type = $this->column->getType();
97
        return TDBMDaoGenerator::isSerializableType($type);
0 ignored issues
show
Bug Best Practice introduced by
The expression return TheCodingMachine\...SerializableType($type) returns the type boolean which is incompatible with the type-hinted return string.
Loading history...
98
    }
99
100
    /**
101
     * Returns true if the property is compulsory (and therefore should be fetched in the constructor).
102
     *
103
     * @return bool
104
     */
105
    public function isCompulsory()
106
    {
107
        return $this->column->getNotnull() && !$this->isAutoincrement() && $this->column->getDefault() === null && !$this->hasUuidAnnotation();
108
    }
109
110
    private function isAutoincrement() : bool
111
    {
112
        return $this->column->getAutoincrement() || $this->getAutoincrementAnnotation() !== null;
113
    }
114
115
    private function hasUuidAnnotation(): bool
116
    {
117
        return $this->getUuidAnnotation() !== null;
118
    }
119
120
    private function getUuidAnnotation(): ?Annotation
121
    {
122
        return $this->getAnnotations()->findAnnotation('UUID');
123
    }
124
125
    private function getAutoincrementAnnotation(): ?Annotation
126
    {
127
        return $this->getAnnotations()->findAnnotation('Autoincrement');
128
    }
129
130
    private function getAnnotations(): Annotations
131
    {
132
        if ($this->annotations === null) {
133
            $comment = $this->column->getComment();
134
            if ($comment === null) {
135
                return new Annotations([]);
136
            }
137
            $parser = new AnnotationParser();
138
            $this->annotations = $parser->parse($comment);
139
        }
140
        return $this->annotations;
141
    }
142
143
    /**
144
     * Returns true if the property has a default value (or if the @UUID annotation is set for the column)
145
     *
146
     * @return bool
147
     */
148
    public function hasDefault()
149
    {
150
        // MariaDB 10.3 issue: it returns "NULL" (the string) instead of *null*
151
        return ($this->column->getDefault() !== null && $this->column->getDefault() !== 'NULL') || $this->hasUuidAnnotation();
152
    }
153
154
    /**
155
     * Returns the code that assigns a value to its default value.
156
     *
157
     * @return string
158
     */
159
    public function assignToDefaultCode()
160
    {
161
        $str = '        $this->%s(%s);';
162
163
        $uuidAnnotation = $this->getUuidAnnotation();
164
        if ($uuidAnnotation !== null) {
165
            $defaultCode = $this->getUuidCode($uuidAnnotation);
166
        } else {
167
            $default = $this->column->getDefault();
168
            $type = $this->column->getType();
169
170
            if (in_array($type->getName(), [
171
                'datetime',
172
                'datetime_immutable',
173
                'datetimetz',
174
                'datetimetz_immutable',
175
                'date',
176
                'date_immutable',
177
                'time',
178
                'time_immutable',
179
            ], true)) {
180
                if (in_array(strtoupper($default), ['CURRENT_TIMESTAMP' /* MySQL */, 'NOW()' /* PostgreSQL */, 'SYSDATE' /* Oracle */ , 'CURRENT_TIMESTAMP()' /* MariaDB 10.3 */], true)) {
181
                    $defaultCode = 'new \DateTimeImmutable()';
182
                } else {
183
                    throw new TDBMException('Unable to set default value for date. Database passed this default value: "'.$default.'"');
184
                }
185
            } else {
186
                $defaultCode = var_export($this->column->getDefault(), true);
187
            }
188
        }
189
190
        return sprintf($str, $this->getSetterName(), $defaultCode);
191
    }
192
193
    private function getUuidCode(Annotation $uuidAnnotation): string
194
    {
195
        $comment = trim($uuidAnnotation->getAnnotationComment(), '\'"');
196
        switch ($comment) {
197
            case '':
198
            case 'v1':
199
                return '(string) Uuid::uuid1()';
200
            case 'v4':
201
                return '(string) Uuid::uuid4()';
202
            default:
203
                throw new TDBMException('@UUID annotation accepts either "v1" or "v4" parameter. Unexpected parameter: ' . $comment);
204
        }
205
    }
206
207
    /**
208
     * Returns true if the property is the primary key.
209
     *
210
     * @return bool
211
     */
212
    public function isPrimaryKey()
213
    {
214
        return in_array($this->column->getName(), $this->table->getPrimaryKey()->getUnquotedColumns());
215
    }
216
217
    /**
218
     * Returns the PHP code for getters and setters.
219
     *
220
     * @return string
221
     */
222
    public function getGetterSetterCode()
223
    {
224
        $normalizedType = $this->getPhpType();
225
226
        $columnGetterName = $this->getGetterName();
227
        $columnSetterName = $this->getSetterName();
228
229
        // A column type can be forced if it is not nullable and not auto-incrementable (for auto-increment columns, we can get "null" as long as the bean is not saved).
230
        $isNullable = !$this->column->getNotnull() || $this->isAutoincrement();
231
232
        $getterAndSetterCode = '    /**
233
     * The getter for the "%s" column.
234
     *
235
     * @return %s
236
     */
237
    public function %s() : %s%s
238
    {
239
        return $this->get(%s, %s);
240
    }
241
242
    /**
243
     * The setter for the "%s" column.
244
     *
245
     * @param %s $%s
246
     */
247
    public function %s(%s%s $%s) : void
248
    {
249
        $this->set(%s, $%s, %s);
250
    }
251
252
';
253
254
        return sprintf(
255
            $getterAndSetterCode,
256
            // Getter
257
            $this->column->getName(),
258
            $normalizedType.($isNullable ? '|null' : ''),
259
            $columnGetterName,
260
            ($isNullable ? '?' : ''),
261
            $normalizedType,
262
            var_export($this->column->getName(), true),
263
            var_export($this->table->getName(), true),
264
            // Setter
265
            $this->column->getName(),
266
            $normalizedType.($isNullable ? '|null' : ''),
267
            $this->column->getName(),
268
            $columnSetterName,
269
            $this->column->getNotnull() ? '' : '?',
270
            $normalizedType,
271
                //$castTo,
272
            $this->column->getName(),
273
            var_export($this->column->getName(), true),
274
            $this->column->getName(),
275
            var_export($this->table->getName(), true)
276
        );
277
    }
278
279
    /**
280
     * Returns the part of code useful when doing json serialization.
281
     *
282
     * @return string
283
     */
284
    public function getJsonSerializeCode()
285
    {
286
        $normalizedType = $this->getPhpType();
287
288
//        if (!$this->canBeSerialized()){
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
289
//            return '';
290
//        }
291
292
        if ($normalizedType == '\\DateTimeImmutable') {
293
            return '        $array['.var_export($this->namingStrategy->getJsonProperty($this), true).'] = ($this->'.$this->getGetterName().'() === null) ? null : $this->'.$this->getGetterName()."()->format('c');\n";
294
        } else {
295
            return '        $array['.var_export($this->namingStrategy->getJsonProperty($this), true).'] = $this->'.$this->getGetterName()."();\n";
296
        }
297
    }
298
299
    /**
300
     * Returns the column name.
301
     *
302
     * @return string
303
     */
304
    public function getColumnName()
305
    {
306
        return $this->column->getName();
307
    }
308
309
    /**
310
     * The code to past in the __clone method.
311
     * @return null|string
312
     */
313
    public function getCloneRule(): ?string
314
    {
315
        $uuidAnnotation = $this->getUuidAnnotation();
316
        if ($uuidAnnotation !== null && $this->isPrimaryKey()) {
317
            return sprintf("        \$this->%s(%s);\n", $this->getSetterName(), $this->getUuidCode($uuidAnnotation));
318
        }
319
        return null;
320
    }
321
}
322