Passed
Pull Request — master (#49)
by David
03:02
created

ScalarBeanPropertyDescriptor   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
dl 0
loc 273
rs 8.6
c 0
b 0
f 0
wmc 37
lcom 1
cbo 10

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getForeignKey() 0 4 1
A getParamAnnotation() 0 8 1
A getClassName() 0 4 1
A getPhpType() 0 5 1
A isCompulsory() 0 4 4
A isAutoincrement() 0 4 2
A hasUuidAnnotation() 0 4 1
A getUuidAnnotation() 0 4 1
A getAutoincrementAnnotation() 0 4 1
A getAnnotations() 0 12 3
A hasDefault() 0 5 3
C assignToDefaultCode() 0 45 7
A isPrimaryKey() 0 4 1
B getGetterSetterCode() 0 56 6
A getJsonSerializeCode() 0 10 2
A getColumnName() 0 4 1
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;
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 true if the property is compulsory (and therefore should be fetched in the constructor).
91
     *
92
     * @return bool
93
     */
94
    public function isCompulsory()
95
    {
96
        return $this->column->getNotnull() && !$this->isAutoincrement() && $this->column->getDefault() === null && !$this->hasUuidAnnotation();
97
    }
98
99
    private function isAutoincrement() : bool
100
    {
101
        return $this->column->getAutoincrement() || $this->getAutoincrementAnnotation() !== null;
102
    }
103
104
    private function hasUuidAnnotation(): bool
105
    {
106
        return $this->getUuidAnnotation() !== null;
107
    }
108
109
    private function getUuidAnnotation(): ?Annotation
110
    {
111
        return $this->getAnnotations()->findAnnotation('UUID');
112
    }
113
114
    private function getAutoincrementAnnotation(): ?Annotation
115
    {
116
        return $this->getAnnotations()->findAnnotation('Autoincrement');
117
    }
118
119
    private function getAnnotations(): Annotations
120
    {
121
        if ($this->annotations === null) {
122
            $comment = $this->column->getComment();
123
            if ($comment === null) {
124
                return new Annotations([]);
125
            }
126
            $parser = new AnnotationParser();
127
            $this->annotations = $parser->parse($comment);
128
        }
129
        return $this->annotations;
130
    }
131
132
    /**
133
     * Returns true if the property has a default value (or if the @UUID annotation is set for the column)
134
     *
135
     * @return bool
136
     */
137
    public function hasDefault()
138
    {
139
        // MariaDB 10.3 issue: it returns "NULL" (the string) instead of *null*
140
        return ($this->column->getDefault() !== null && $this->column->getDefault() !== 'NULL') || $this->hasUuidAnnotation();
141
    }
142
143
    /**
144
     * Returns the code that assigns a value to its default value.
145
     *
146
     * @return string
147
     */
148
    public function assignToDefaultCode()
149
    {
150
        $str = '        $this->%s(%s);';
151
152
        $uuidAnnotation = $this->getUuidAnnotation();
153
        if ($uuidAnnotation !== null) {
154
            $comment = trim($uuidAnnotation->getAnnotationComment(), '\'"');
155
            switch ($comment) {
156
                case '':
157
                case 'v1':
158
                    $defaultCode = '(string) Uuid::uuid1()';
159
                    break;
160
                case 'v4':
161
                    $defaultCode = '(string) Uuid::uuid4()';
162
                    break;
163
                default:
164
                    throw new TDBMException('@UUID annotation accepts either "v1" or "v4" parameter. Unexpected parameter: '.$comment);
165
            }
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
191
        return sprintf($str, $this->getSetterName(), $defaultCode);
192
    }
193
194
    /**
195
     * Returns true if the property is the primary key.
196
     *
197
     * @return bool
198
     */
199
    public function isPrimaryKey()
200
    {
201
        return in_array($this->column->getName(), $this->table->getPrimaryKey()->getUnquotedColumns());
202
    }
203
204
    /**
205
     * Returns the PHP code for getters and setters.
206
     *
207
     * @return string
208
     */
209
    public function getGetterSetterCode()
210
    {
211
        $normalizedType = $this->getPhpType();
212
213
        $columnGetterName = $this->getGetterName();
214
        $columnSetterName = $this->getSetterName();
215
216
        // 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).
217
        $isNullable = !$this->column->getNotnull() || $this->isAutoincrement();
218
219
        $getterAndSetterCode = '    /**
220
     * The getter for the "%s" column.
221
     *
222
     * @return %s
223
     */
224
    public function %s() : %s%s
225
    {
226
        return $this->get(%s, %s);
227
    }
228
229
    /**
230
     * The setter for the "%s" column.
231
     *
232
     * @param %s $%s
233
     */
234
    public function %s(%s%s $%s) : void
235
    {
236
        $this->set(%s, $%s, %s);
237
    }
238
239
';
240
241
        return sprintf(
242
            $getterAndSetterCode,
243
            // Getter
244
            $this->column->getName(),
245
            $normalizedType.($isNullable ? '|null' : ''),
246
            $columnGetterName,
247
            ($isNullable ? '?' : ''),
248
            $normalizedType,
249
            var_export($this->column->getName(), true),
250
            var_export($this->table->getName(), true),
251
            // Setter
252
            $this->column->getName(),
253
            $normalizedType.($isNullable ? '|null' : ''),
254
            $this->column->getName(),
255
            $columnSetterName,
256
            $this->column->getNotnull() ? '' : '?',
257
            $normalizedType,
258
                //$castTo,
259
            $this->column->getName(),
260
            var_export($this->column->getName(), true),
261
            $this->column->getName(),
262
            var_export($this->table->getName(), true)
263
        );
264
    }
265
266
    /**
267
     * Returns the part of code useful when doing json serialization.
268
     *
269
     * @return string
270
     */
271
    public function getJsonSerializeCode()
272
    {
273
        $normalizedType = $this->getPhpType();
274
275
        if ($normalizedType == '\\DateTimeImmutable') {
276
            return '        $array['.var_export($this->namingStrategy->getJsonProperty($this), true).'] = ($this->'.$this->getGetterName().'() === null) ? null : $this->'.$this->getGetterName()."()->format('c');\n";
277
        } else {
278
            return '        $array['.var_export($this->namingStrategy->getJsonProperty($this), true).'] = $this->'.$this->getGetterName()."();\n";
279
        }
280
    }
281
282
    /**
283
     * Returns the column name.
284
     *
285
     * @return string
286
     */
287
    public function getColumnName()
288
    {
289
        return $this->column->getName();
290
    }
291
}
292