Completed
Pull Request — master (#125)
by David
04:02
created

ObjectBeanPropertyDescriptor::isSetterProtected()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 0
1
<?php
2
declare(strict_types=1);
3
4
namespace TheCodingMachine\TDBM\Utils;
5
6
use function array_map;
7
use Doctrine\DBAL\Schema\Column;
8
use Doctrine\DBAL\Schema\Table;
9
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
10
use TheCodingMachine\TDBM\TDBMException;
11
use TheCodingMachine\TDBM\Utils\Annotation\AnnotationParser;
12
use TheCodingMachine\TDBM\Utils\Annotation\Annotations;
13
use Zend\Code\Generator\AbstractMemberGenerator;
14
use Zend\Code\Generator\MethodGenerator;
15
use Zend\Code\Generator\ParameterGenerator;
16
17
/**
18
 * This class represent a property in a bean that points to another table.
19
 */
20
class ObjectBeanPropertyDescriptor extends AbstractBeanPropertyDescriptor
21
{
22
    use ForeignKeyAnalyzerTrait;
23
24
    /**
25
     * @var ForeignKeyConstraint
26
     */
27
    private $foreignKey;
28
    /**
29
     * @var string
30
     */
31
    private $beanNamespace;
32
33
    /**
34
     * @var AnnotationParser
35
     */
36
    private $annotationParser;
37
38
    /**
39
     * ObjectBeanPropertyDescriptor constructor.
40
     * @param Table $table
41
     * @param ForeignKeyConstraint $foreignKey
42
     * @param NamingStrategyInterface $namingStrategy
43
     */
44
    public function __construct(Table $table, ForeignKeyConstraint $foreignKey, NamingStrategyInterface $namingStrategy, string $beanNamespace, AnnotationParser $annotationParser)
45
    {
46
        parent::__construct($table, $namingStrategy);
47
        $this->foreignKey = $foreignKey;
48
        $this->beanNamespace = $beanNamespace;
49
        $this->annotationParser = $annotationParser;
50
    }
51
52
    /**
53
     * Returns the foreignkey the column is part of, if any. null otherwise.
54
     *
55
     * @return ForeignKeyConstraint
56
     */
57
    public function getForeignKey(): ForeignKeyConstraint
58
    {
59
        return $this->foreignKey;
60
    }
61
62
    /**
63
     * Returns the name of the class linked to this property or null if this is not a foreign key.
64
     *
65
     * @return string
66
     */
67
    public function getClassName(): string
68
    {
69
        return $this->namingStrategy->getBeanClassName($this->foreignKey->getForeignTableName());
70
    }
71
72
    /**
73
     * Returns the PHP type for the property (it can be a scalar like int, bool, or class names, like \DateTimeInterface, App\Bean\User....)
74
     *
75
     * @return string
76
     */
77
    public function getPhpType(): string
78
    {
79
        return '\\'.$this->beanNamespace.'\\'.$this->getClassName();
80
    }
81
82
    /**
83
     * Returns true if the property is compulsory (and therefore should be fetched in the constructor).
84
     *
85
     * @return bool
86
     */
87
    public function isCompulsory(): bool
88
    {
89
        // Are all columns nullable?
90
        foreach ($this->getLocalColumns() as $column) {
91
            if ($column->getNotnull()) {
92
                return true;
93
            }
94
        }
95
96
        return false;
97
    }
98
99
    /**
100
     * Returns true if the property has a default value.
101
     *
102
     * @return bool
103
     */
104
    public function hasDefault(): bool
105
    {
106
        return false;
107
    }
108
109
    /**
110
     * Returns the code that assigns a value to its default value.
111
     *
112
     * @return string
113
     *
114
     * @throws TDBMException
115
     */
116
    public function assignToDefaultCode(): string
117
    {
118
        throw new TDBMException('Foreign key based properties cannot be assigned a default value.');
119
    }
120
121
    /**
122
     * Returns true if the property is the primary key.
123
     *
124
     * @return bool
125
     */
126
    public function isPrimaryKey(): bool
127
    {
128
        $fkColumns = $this->foreignKey->getUnquotedLocalColumns();
129
        sort($fkColumns);
130
131
        $pkColumns = TDBMDaoGenerator::getPrimaryKeyColumnsOrFail($this->table);
132
        sort($pkColumns);
133
134
        return $fkColumns == $pkColumns;
135
    }
136
137
    /**
138
     * Returns the PHP code for getters and setters.
139
     *
140
     * @return MethodGenerator[]
141
     */
142
    public function getGetterSetterCode(): array
143
    {
144
        $tableName = $this->table->getName();
145
        $getterName = $this->getGetterName();
146
        $setterName = $this->getSetterName();
147
        $isNullable = !$this->isCompulsory();
148
149
        $referencedBeanName = $this->namingStrategy->getBeanClassName($this->foreignKey->getForeignTableName());
150
151
        $getter = new MethodGenerator($getterName);
152
        $getter->setDocBlock('Returns the '.$referencedBeanName.' object bound to this object via the '.implode(' and ', $this->foreignKey->getUnquotedLocalColumns()).' column.');
153
154
        /*$types = [ $referencedBeanName ];
155
        if ($isNullable) {
156
            $types[] = 'null';
157
        }
158
        $getter->getDocBlock()->setTag(new ReturnTag($types));*/
159
160
        $getter->setReturnType(($isNullable?'?':'').$this->beanNamespace.'\\'.$referencedBeanName);
161
162
        $getter->setBody('return $this->getRef('.var_export($this->foreignKey->getName(), true).', '.var_export($tableName, true).');');
163
164
        if ($this->isGetterProtected()) {
165
            $getter->setVisibility(AbstractMemberGenerator::VISIBILITY_PROTECTED);
166
        }
167
168
        $setter = new MethodGenerator($setterName);
169
        $setter->setDocBlock('The setter for the '.$referencedBeanName.' object bound to this object via the '.implode(' and ', $this->foreignKey->getUnquotedLocalColumns()).' column.');
170
171
        $setter->setParameter(new ParameterGenerator('object', ($isNullable?'?':'').$this->beanNamespace.'\\'.$referencedBeanName));
172
173
        $setter->setReturnType('void');
174
175
        $setter->setBody('$this->setRef('.var_export($this->foreignKey->getName(), true).', $object, '.var_export($tableName, true).');');
176
177
        if ($this->isSetterProtected()) {
178
            $setter->setVisibility(AbstractMemberGenerator::VISIBILITY_PROTECTED);
179
        }
180
181
        return [$getter, $setter];
182
    }
183
184
    /**
185
     * Returns the part of code useful when doing json serialization.
186
     *
187
     * @return string
188
     */
189
    public function getJsonSerializeCode(): string
190
    {
191
        if ($this->isGetterProtected()) {
192
            return '';
193
        }
194
195
        if (!$this->isCompulsory()) {
196
            return 'if (!$stopRecursion) {
197
    $object = $this->'.$this->getGetterName().'();
198
    $array['.var_export($this->namingStrategy->getJsonProperty($this), true).'] = $object ? $object->jsonSerialize(true) : null;
199
}
200
';
201
        } else {
202
            return 'if (!$stopRecursion) {
203
    $array['.var_export($this->namingStrategy->getJsonProperty($this), true).'] = $this->'.$this->getGetterName().'()->jsonSerialize(true);
204
}
205
';
206
        }
207
    }
208
209
    /**
210
     * The code to past in the __clone method.
211
     * @return null|string
212
     */
213
    public function getCloneRule(): ?string
214
    {
215
        return null;
216
    }
217
218
    /**
219
     * Tells if this property is a type-hintable in PHP (resource isn't for example)
220
     *
221
     * @return bool
222
     */
223
    public function isTypeHintable() : bool
224
    {
225
        return true;
226
    }
227
228
    private function isGetterProtected(): bool
229
    {
230
        foreach ($this->getAnnotations() as $annotations) {
231
            if ($annotations->findAnnotation(Annotation\ProtectedGetter::class)) {
232
                return true;
233
            }
234
        }
235
        return false;
236
    }
237
238
    private function isSetterProtected(): bool
239
    {
240
        foreach ($this->getAnnotations() as $annotations) {
241
            if ($annotations->findAnnotation(Annotation\ProtectedSetter::class)) {
242
                return true;
243
            }
244
        }
245
        return false;
246
    }
247
248
}
249